14625.1.1
by Steve Kowalik
Hide SPRBs that the user can't see in the SPRecipe views. |
1 |
# Copyright 2010-2012 Canonical Ltd. This software is licensed under the
|
10498.3.1
by Paul Hummer
Added empty browser file for ISourcePackageRecipe |
2 |
# GNU Affero General Public License version 3 (see the file LICENSE).
|
3 |
||
4 |
"""SourcePackageRecipe views."""
|
|
5 |
||
6 |
__metaclass__ = type |
|
7 |
||
7675.618.39
by Paul Hummer
Added a recipe edit view. |
8 |
__all__ = [ |
7675.618.40
by Paul Hummer
Fixed __all__ in lp.code.browser.sourcepackagerecipe |
9 |
'SourcePackageRecipeAddView', |
10 |
'SourcePackageRecipeContextMenu', |
|
7675.618.45
by Paul Hummer
Fixed some lint |
11 |
'SourcePackageRecipeEditView', |
7675.618.39
by Paul Hummer
Added a recipe edit view. |
12 |
'SourcePackageRecipeNavigationMenu', |
7675.618.40
by Paul Hummer
Fixed __all__ in lp.code.browser.sourcepackagerecipe |
13 |
'SourcePackageRecipeRequestBuildsView', |
14 |
'SourcePackageRecipeView', |
|
7675.618.39
by Paul Hummer
Added a recipe edit view. |
15 |
]
|
10498.3.6
by Aaron Bentley
Initial cut of index page. |
16 |
|
12373.2.1
by Tim Penhey
First hack. |
17 |
import itertools |
12013.4.10
by Ian Booth
Lint issues |
18 |
|
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
19 |
from bzrlib.plugins.builder.recipe import ( |
11476.1.2
by Aaron Bentley
Use permitted_instructions when parsing. |
20 |
ForbiddenInstructionError, |
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
21 |
RecipeParseError, |
22 |
RecipeParser, |
|
23 |
)
|
|
7675.622.3
by Paul Hummer
Added update notification code. |
24 |
from lazr.lifecycle.event import ObjectModifiedEvent |
25 |
from lazr.lifecycle.snapshot import Snapshot |
|
13314.13.2
by Ian Booth
Lint |
26 |
from lazr.restful.interface import ( |
27 |
copy_field, |
|
28 |
use_template, |
|
29 |
)
|
|
12547.1.5
by Ian Booth
Fix imports |
30 |
from lazr.restful.interfaces import ( |
31 |
IFieldHTMLRenderer, |
|
32 |
IWebServiceClientRequest, |
|
33 |
)
|
|
12442.2.9
by j.c.sackett
Ran import reformatter per review. |
34 |
import simplejson |
11043.2.4
by Paul Hummer
No commits in view code |
35 |
from storm.locals import Store |
11994.2.16
by Ian Booth
Add new tests and code to allow for branchesrelated to source packages as well as products |
36 |
from z3c.ptcompat import ViewPageTemplateFile |
12547.1.5
by Ian Booth
Fix imports |
37 |
from zope import component |
11994.2.16
by Ian Booth
Add new tests and code to allow for branchesrelated to source packages as well as products |
38 |
from zope.app.form.browser.widget import Widget |
39 |
from zope.app.form.interfaces import IView |
|
10498.5.1
by Aaron Bentley
Get distroseries listing displaying. |
40 |
from zope.component import getUtility |
7675.622.3
by Paul Hummer
Added update notification code. |
41 |
from zope.event import notify |
11814.1.2
by Paul Hummer
Fixed teh bug |
42 |
from zope.formlib import form |
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
43 |
from zope.interface import ( |
12547.1.5
by Ian Booth
Fix imports |
44 |
implementer, |
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
45 |
implements, |
46 |
Interface, |
|
47 |
providedBy, |
|
48 |
)
|
|
49 |
from zope.schema import ( |
|
12442.2.9
by j.c.sackett
Ran import reformatter per review. |
50 |
Choice, |
11994.2.16
by Ian Booth
Add new tests and code to allow for branchesrelated to source packages as well as products |
51 |
Field, |
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
52 |
List, |
53 |
Text, |
|
11929.11.17
by Tim Penhey
Rework the new page to allow the creation of a ppa on the fly. |
54 |
TextLine, |
55 |
)
|
|
12547.1.5
by Ian Booth
Fix imports |
56 |
from zope.schema.interfaces import ICollection |
11929.11.17
by Tim Penhey
Rework the new page to allow the creation of a ppa on the fly. |
57 |
from zope.schema.vocabulary import ( |
58 |
SimpleTerm, |
|
59 |
SimpleVocabulary, |
|
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
60 |
)
|
12599.4.2
by Leonard Richardson
Merge from trunk. |
61 |
from zope.security.proxy import isinstance as zope_isinstance |
10498.5.1
by Aaron Bentley
Get distroseries listing displaying. |
62 |
|
14600.1.12
by Curtis Hovey
Move i18n to lp. |
63 |
from lp import _ |
13130.1.12
by Curtis Hovey
Sorted imports. |
64 |
from lp.app.browser.launchpad import Hierarchy |
11929.9.1
by Tim Penhey
Move launchpadform into lp.app.browser. |
65 |
from lp.app.browser.launchpadform import ( |
66 |
action, |
|
67 |
custom_widget, |
|
11929.11.6
by Tim Penhey
Allow forms to specify that some fields have structured widget help. |
68 |
has_structured_doc, |
11929.9.1
by Tim Penhey
Move launchpadform into lp.app.browser. |
69 |
LaunchpadEditFormView, |
70 |
LaunchpadFormView, |
|
11929.11.17
by Tim Penhey
Rework the new page to allow the creation of a ppa on the fly. |
71 |
render_radio_widget_part, |
11929.9.1
by Tim Penhey
Move launchpadform into lp.app.browser. |
72 |
)
|
12261.2.2
by Tim Penhey
Merge the refactoring branch, and fix the new declarations. |
73 |
from lp.app.browser.lazrjs import ( |
12344.2.1
by Tim Penhey
Add boolean choice widget. |
74 |
BooleanChoiceWidget, |
12261.2.2
by Tim Penhey
Merge the refactoring branch, and fix the new declarations. |
75 |
InlineEditPickerWidget, |
13429.1.2
by Ian Booth
Add meta attribute support to picker widgets |
76 |
InlinePersonEditPickerWidget, |
12261.2.2
by Tim Penhey
Merge the refactoring branch, and fix the new declarations. |
77 |
TextAreaEditorWidget, |
12421.3.1
by Tim Penhey
Add inline editing of the recipe name. |
78 |
TextLineEditorWidget, |
12261.2.2
by Tim Penhey
Merge the refactoring branch, and fix the new declarations. |
79 |
)
|
12547.1.7
by Ian Booth
Refactor spr interface and improve rendering |
80 |
from lp.app.browser.tales import format_link |
12442.2.9
by j.c.sackett
Ran import reformatter per review. |
81 |
from lp.app.validators.name import name_validator |
12293.1.10
by Curtis Hovey
Formatted imports. |
82 |
from lp.app.widgets.itemswidgets import ( |
83 |
LabeledMultiCheckBoxWidget, |
|
84 |
LaunchpadRadioWidget, |
|
85 |
)
|
|
86 |
from lp.app.widgets.suggestion import RecipeOwnerWidget |
|
11236.1.2
by Aaron Bentley
Handle PrivateBranchRecipe as a user error in the web UI. |
87 |
from lp.code.errors import ( |
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
88 |
BuildAlreadyPending, |
89 |
NoSuchBranch, |
|
90 |
PrivateBranchRecipe, |
|
13824.2.1
by Brad Crittenden
Catch TooManyBuild exception rather than OOPS |
91 |
TooManyBuilds, |
11262.3.2
by Curtis Hovey
Merged devel. |
92 |
TooNewRecipeFormat, |
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
93 |
)
|
11994.2.21
by Ian Booth
Refactor related branches methods to IBranchTarget and show distro series instead of source package for related package branches |
94 |
from lp.code.interfaces.branchtarget import IBranchTarget |
10744.5.3
by Paul Hummer
Got a working create recipe flow |
95 |
from lp.code.interfaces.sourcepackagerecipe import ( |
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
96 |
ISourcePackageRecipe, |
97 |
ISourcePackageRecipeSource, |
|
98 |
MINIMAL_RECIPE_TEXT, |
|
12373.1.3
by Tim Penhey
Lint cleanup. |
99 |
)
|
12599.4.2
by Leonard Richardson
Merge from trunk. |
100 |
from lp.code.model.branchtarget import PersonBranchTarget |
12373.2.8
by Tim Penhey
Set initial distroseries, and make description required again (needs db patch to remove not null constraint). |
101 |
from lp.code.model.sourcepackagerecipe import get_buildable_distroseries_set |
102 |
from lp.registry.interfaces.series import SeriesStatus |
|
13378.1.1
by j.c.sackett
Undid the rollback by wgrant to get the functionality we need from wallyworld's branch. |
103 |
from lp.services.fields import PersonChoice |
11994.2.16
by Ian Booth
Add new tests and code to allow for branchesrelated to source packages as well as products |
104 |
from lp.services.propertycache import cachedproperty |
14612.2.1
by William Grant
format-imports on lib/. So many imports. |
105 |
from lp.services.webapp import ( |
106 |
canonical_url, |
|
107 |
ContextMenu, |
|
108 |
enabled_with_permission, |
|
109 |
LaunchpadView, |
|
110 |
Link, |
|
111 |
NavigationMenu, |
|
112 |
structured, |
|
113 |
)
|
|
114 |
from lp.services.webapp.authorization import check_permission |
|
115 |
from lp.services.webapp.breadcrumb import Breadcrumb |
|
13912.4.2
by Aaron Bentley
Handle ArchiveDisabled as user error. |
116 |
from lp.soyuz.interfaces.archive import ArchiveDisabled |
11929.11.17
by Tim Penhey
Rework the new page to allow the creation of a ppa on the fly. |
117 |
from lp.soyuz.model.archive import Archive |
10498.3.22
by Aaron Bentley
Restrict the number of old builds shown. |
118 |
|
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
119 |
|
7675.618.59
by Paul Hummer
Responded to Tim's review |
120 |
class IRecipesForPerson(Interface): |
7675.618.56
by Paul Hummer
Got the breadcrumbs working. |
121 |
"""A marker interface for source package recipe sets."""
|
122 |
||
123 |
||
7675.618.59
by Paul Hummer
Responded to Tim's review |
124 |
class RecipesForPersonBreadcrumb(Breadcrumb): |
10788.2.2
by Aaron Bentley
Fix lint errors. |
125 |
"""A Breadcrumb to handle the "Recipes" link for recipe breadcrumbs."""
|
7675.618.56
by Paul Hummer
Got the breadcrumbs working. |
126 |
|
127 |
rootsite = 'code' |
|
128 |
text = 'Recipes' |
|
129 |
||
7675.618.59
by Paul Hummer
Responded to Tim's review |
130 |
implements(IRecipesForPerson) |
131 |
||
7675.618.56
by Paul Hummer
Got the breadcrumbs working. |
132 |
@property
|
133 |
def url(self): |
|
11318.8.9
by Tim Penhey
Root site of code needed on recipe views. |
134 |
return canonical_url( |
135 |
self.context, view_name="+recipes", rootsite='code') |
|
7675.618.56
by Paul Hummer
Got the breadcrumbs working. |
136 |
|
137 |
||
7675.618.55
by Paul Hummer
Added breadcrumb code for the source package recipes |
138 |
class SourcePackageRecipeHierarchy(Hierarchy): |
13824.2.1
by Brad Crittenden
Catch TooManyBuild exception rather than OOPS |
139 |
"""Hierarchy for Source Package Recipe."""
|
7675.618.55
by Paul Hummer
Added breadcrumb code for the source package recipes |
140 |
|
141 |
vhost_breadcrumb = False |
|
142 |
||
143 |
@property
|
|
144 |
def objects(self): |
|
145 |
"""See `Hierarchy`."""
|
|
146 |
traversed = list(self.request.traversed_objects) |
|
147 |
||
148 |
# Pop the root object
|
|
149 |
yield traversed.pop(0) |
|
150 |
||
151 |
recipe = traversed.pop(0) |
|
152 |
while not ISourcePackageRecipe.providedBy(recipe): |
|
153 |
yield recipe |
|
154 |
recipe = traversed.pop(0) |
|
7675.618.56
by Paul Hummer
Got the breadcrumbs working. |
155 |
|
156 |
# Pop in the "Recipes" link to recipe listings.
|
|
7675.618.59
by Paul Hummer
Responded to Tim's review |
157 |
yield RecipesForPersonBreadcrumb(recipe.owner) |
7675.618.55
by Paul Hummer
Added breadcrumb code for the source package recipes |
158 |
yield recipe |
159 |
||
160 |
for item in traversed: |
|
161 |
yield item |
|
162 |
||
163 |
||
7675.618.39
by Paul Hummer
Added a recipe edit view. |
164 |
class SourcePackageRecipeNavigationMenu(NavigationMenu): |
165 |
"""Navigation menu for sourcepackage recipes."""
|
|
166 |
||
167 |
usedfor = ISourcePackageRecipe |
|
168 |
||
169 |
facet = 'branches' |
|
170 |
||
7675.618.47
by Paul Hummer
Added delete view |
171 |
links = ('edit', 'delete') |
7675.618.39
by Paul Hummer
Added a recipe edit view. |
172 |
|
173 |
@enabled_with_permission('launchpad.Edit') |
|
174 |
def edit(self): |
|
175 |
return Link('+edit', 'Edit recipe', icon='edit') |
|
176 |
||
7675.618.47
by Paul Hummer
Added delete view |
177 |
@enabled_with_permission('launchpad.Edit') |
178 |
def delete(self): |
|
179 |
return Link('+delete', 'Delete recipe', icon='trash-icon') |
|
180 |
||
7675.618.39
by Paul Hummer
Added a recipe edit view. |
181 |
|
10498.6.1
by Aaron Bentley
Use table for build listing, generate link properly. |
182 |
class SourcePackageRecipeContextMenu(ContextMenu): |
183 |
"""Context menu for sourcepackage recipes."""
|
|
184 |
||
185 |
usedfor = ISourcePackageRecipe |
|
186 |
||
187 |
facet = 'branches' |
|
188 |
||
12378.2.3
by Ian Booth
Initial implementation |
189 |
links = ('request_builds', 'request_daily_build',) |
10498.6.1
by Aaron Bentley
Use table for build listing, generate link properly. |
190 |
|
191 |
def request_builds(self): |
|
10498.5.16
by Aaron Bentley
Update docs |
192 |
"""Provide a link for requesting builds of a recipe."""
|
10498.6.1
by Aaron Bentley
Use table for build listing, generate link properly. |
193 |
return Link('+request-builds', 'Request build(s)', icon='add') |
194 |
||
12378.2.3
by Ian Booth
Initial implementation |
195 |
def request_daily_build(self): |
196 |
"""Provide a link for requesting a daily build of a recipe."""
|
|
197 |
recipe = self.context |
|
198 |
ppa = recipe.daily_build_archive |
|
13912.4.1
by Aaron Bentley
Disabling archive disables daily build link. |
199 |
if (ppa is None or not ppa.enabled or not recipe.build_daily or not |
200 |
recipe.is_stale or not recipe.distroseries): |
|
12378.2.3
by Ian Booth
Initial implementation |
201 |
show_request_build = False |
202 |
else: |
|
203 |
has_upload = ppa.checkArchivePermission(recipe.owner) |
|
204 |
show_request_build = has_upload |
|
205 |
||
13429.1.3
by Ian Booth
Lint |
206 |
show_request_build = (show_request_build and |
12941.2.1
by Ian Booth
Do not show recipie build now if user does not have edit permission on recipe |
207 |
check_permission('launchpad.Edit', recipe)) |
12378.2.3
by Ian Booth
Initial implementation |
208 |
return Link( |
209 |
'+request-daily-build', 'Build now', |
|
210 |
enabled=show_request_build) |
|
211 |
||
10498.6.1
by Aaron Bentley
Use table for build listing, generate link properly. |
212 |
|
10498.3.6
by Aaron Bentley
Initial cut of index page. |
213 |
class SourcePackageRecipeView(LaunchpadView): |
214 |
"""Default view of a SourcePackageRecipe."""
|
|
215 |
||
11049.5.2
by Paul Hummer
Added Warning notification |
216 |
def initialize(self): |
217 |
super(SourcePackageRecipeView, self).initialize() |
|
12177.4.1
by Tim Penhey
Show a message on the main recipe page if the recipe owner can't upload into the daily ppa. |
218 |
recipe = self.context |
12221.9.19
by Tim Penhey
Handle missing PPAs in the view. |
219 |
if recipe.build_daily and recipe.daily_build_archive is None: |
220 |
self.request.response.addWarningNotification( |
|
221 |
structured( |
|
222 |
"Daily builds for this recipe will <strong>not</strong> "
|
|
223 |
"occur.<br/><br/>There is no PPA.")) |
|
224 |
elif self.dailyBuildWithoutUploadPermission(): |
|
12177.4.1
by Tim Penhey
Show a message on the main recipe page if the recipe owner can't upload into the daily ppa. |
225 |
self.request.response.addWarningNotification( |
226 |
structured( |
|
227 |
"Daily builds for this recipe will <strong>not</strong> "
|
|
228 |
"occur.<br/><br/>The owner of the recipe (%s) does not " |
|
229 |
"have permission to upload packages into the daily "
|
|
230 |
"build PPA (%s)" % ( |
|
231 |
format_link(recipe.owner), |
|
232 |
format_link(recipe.daily_build_archive)))) |
|
11049.5.2
by Paul Hummer
Added Warning notification |
233 |
|
10498.3.8
by Aaron Bentley
Get index page looking close to intended display. |
234 |
@property
|
7675.618.55
by Paul Hummer
Added breadcrumb code for the source package recipes |
235 |
def page_title(self): |
7675.618.52
by Paul Hummer
Fixed the title |
236 |
return "%(name)s\'s %(recipe_name)s recipe" % { |
237 |
'name': self.context.owner.displayname, |
|
238 |
'recipe_name': self.context.name} |
|
10498.3.8
by Aaron Bentley
Get index page looking close to intended display. |
239 |
|
7675.618.55
by Paul Hummer
Added breadcrumb code for the source package recipes |
240 |
label = page_title |
10498.3.6
by Aaron Bentley
Initial cut of index page. |
241 |
|
10498.3.22
by Aaron Bentley
Restrict the number of old builds shown. |
242 |
@property
|
243 |
def builds(self): |
|
12013.4.6
by Ian Booth
Hook up validation errors |
244 |
return builds_for_recipe(self.context) |
10498.3.22
by Aaron Bentley
Restrict the number of old builds shown. |
245 |
|
12177.4.1
by Tim Penhey
Show a message on the main recipe page if the recipe owner can't upload into the daily ppa. |
246 |
def dailyBuildWithoutUploadPermission(self): |
247 |
"""Returns true if there are upload permissions to the daily archive.
|
|
248 |
||
249 |
If the recipe isn't built daily, we don't consider this a problem.
|
|
250 |
"""
|
|
251 |
recipe = self.context |
|
252 |
ppa = recipe.daily_build_archive |
|
253 |
if recipe.build_daily: |
|
254 |
has_upload = ppa.checkArchivePermission(recipe.owner) |
|
255 |
return not has_upload |
|
256 |
return False |
|
257 |
||
12221.10.2
by Tim Penhey
Move code to use lazrjs.InlineEditPickerWidget. |
258 |
@property
|
259 |
def person_picker(self): |
|
13314.13.1
by Ian Booth
Fix implementation - use custom vocab |
260 |
field = copy_field( |
14174.1.1
by Ian Booth
Remove picker feature flags |
261 |
ISourcePackageRecipe['owner'], |
262 |
vocabularyName='UserTeamsParticipationPlusSelfSimpleDisplay') |
|
13429.1.2
by Ian Booth
Add meta attribute support to picker widgets |
263 |
return InlinePersonEditPickerWidget( |
13314.13.1
by Ian Booth
Fix implementation - use custom vocab |
264 |
self.context, field, |
12221.10.2
by Tim Penhey
Move code to use lazrjs.InlineEditPickerWidget. |
265 |
format_link(self.context.owner), |
266 |
header='Change owner', |
|
267 |
step_title='Select a new owner') |
|
268 |
||
12221.9.21
by Tim Penhey
Move the archive to use the InlineEditPickerWidget. |
269 |
@property
|
270 |
def archive_picker(self): |
|
12268.3.28
by Tim Penhey
Don't allow the removal of a recipe PPA. |
271 |
field = ISourcePackageEditSchema['daily_build_archive'] |
12221.9.21
by Tim Penhey
Move the archive to use the InlineEditPickerWidget. |
272 |
return InlineEditPickerWidget( |
12397.1.5
by Ian Booth
Add support for specifying default values for ObjectFormatterAPI when context is None |
273 |
self.context, field, |
274 |
format_link(self.context.daily_build_archive), |
|
12221.9.21
by Tim Penhey
Move the archive to use the InlineEditPickerWidget. |
275 |
header='Change daily build archive', |
276 |
step_title='Select a PPA') |
|
277 |
||
12261.2.1
by Tim Penhey
initial hack. |
278 |
@property
|
12261.2.9
by Tim Penhey
Style and widget fixes. |
279 |
def recipe_text_widget(self): |
12261.2.1
by Tim Penhey
initial hack. |
280 |
"""The recipe text as widget HTML."""
|
12261.2.2
by Tim Penhey
Merge the refactoring branch, and fix the new declarations. |
281 |
recipe_text = ISourcePackageRecipe['recipe_text'] |
12261.2.9
by Tim Penhey
Style and widget fixes. |
282 |
return TextAreaEditorWidget(self.context, recipe_text, title="") |
12261.2.1
by Tim Penhey
initial hack. |
283 |
|
12261.2.7
by Tim Penhey
Merge daily-ajax. |
284 |
@property
|
12344.2.1
by Tim Penhey
Add boolean choice widget. |
285 |
def daily_build_widget(self): |
286 |
return BooleanChoiceWidget( |
|
287 |
self.context, ISourcePackageRecipe['build_daily'], |
|
12344.2.3
by Tim Penhey
Update the lazr-js-widgets documentation to include the BooleanChoiceWidget. |
288 |
tag='span', |
12344.2.13
by Tim Penhey
Use 'built' rather than 'build' when talking about the build schedule. |
289 |
false_text='Built on request', |
290 |
true_text='Built daily', |
|
12344.2.2
by Tim Penhey
Get it all working. |
291 |
header='Change build schedule') |
12261.2.1
by Tim Penhey
initial hack. |
292 |
|
12261.3.1
by Tim Penhey
Interim work on editing the description. |
293 |
@property
|
294 |
def description_widget(self): |
|
295 |
"""The description as a widget."""
|
|
296 |
description = ISourcePackageRecipe['description'] |
|
297 |
return TextAreaEditorWidget( |
|
298 |
self.context, description, title="") |
|
299 |
||
12421.3.1
by Tim Penhey
Add inline editing of the recipe name. |
300 |
@property
|
301 |
def name_widget(self): |
|
302 |
name = ISourcePackageRecipe['name'] |
|
303 |
title = "Edit the recipe name" |
|
304 |
return TextLineEditorWidget(self.context, name, title, 'h1') |
|
305 |
||
12547.1.2
by Ian Booth
Add inline distro series editing - first cut |
306 |
@property
|
307 |
def distroseries_widget(self): |
|
308 |
from lp.app.browser.lazrjs import InlineMultiCheckboxWidget |
|
309 |
field = ISourcePackageEditSchema['distroseries'] |
|
310 |
return InlineMultiCheckboxWidget( |
|
311 |
self.context, |
|
312 |
field, |
|
313 |
attribute_type="reference", |
|
12547.1.7
by Ian Booth
Refactor spr interface and improve rendering |
314 |
vocabulary='BuildableDistroSeries', |
12547.1.34
by Ian Booth
Text fixes |
315 |
label="Distribution series:", |
12547.1.2
by Ian Booth
Add inline distro series editing - first cut |
316 |
label_tag="dt", |
317 |
header="Change default distribution series:", |
|
318 |
empty_display_value="None", |
|
12547.1.14
by Ian Booth
Fix display order and add windmill test |
319 |
selected_items=sorted( |
320 |
self.context.distroseries, key=lambda ds: ds.displayname), |
|
12547.1.2
by Ian Booth
Add inline distro series editing - first cut |
321 |
items_tag="dd", |
322 |
)
|
|
323 |
||
324 |
||
12547.1.21
by Ian Booth
Lint |
325 |
@component.adapter(ISourcePackageRecipe, ICollection, |
326 |
IWebServiceClientRequest) |
|
12547.1.2
by Ian Booth
Add inline distro series editing - first cut |
327 |
@implementer(IFieldHTMLRenderer) |
328 |
def distroseries_renderer(context, field, request): |
|
12547.1.5
by Ian Booth
Fix imports |
329 |
"""Render a distroseries collection as a set of links."""
|
12547.1.2
by Ian Booth
Add inline distro series editing - first cut |
330 |
|
331 |
def render(value): |
|
12547.1.14
by Ian Booth
Fix display order and add windmill test |
332 |
distroseries = sorted( |
333 |
context.distroseries, key=lambda ds: ds.displayname) |
|
12547.1.9
by Ian Booth
Fix distroseries rendering for empty values |
334 |
if not distroseries: |
335 |
return 'None' |
|
12547.1.6
by Ian Booth
Complete renderer |
336 |
html = "<ul>" |
337 |
html += ''.join( |
|
12547.1.21
by Ian Booth
Lint |
338 |
["<li>%s</li>" % format_link(series) for series in distroseries]) |
12547.1.6
by Ian Booth
Complete renderer |
339 |
html += "</ul>" |
12547.1.2
by Ian Booth
Add inline distro series editing - first cut |
340 |
return html |
341 |
return render |
|
10498.3.8
by Aaron Bentley
Get index page looking close to intended display. |
342 |
|
12547.1.21
by Ian Booth
Lint |
343 |
|
12013.4.6
by Ian Booth
Hook up validation errors |
344 |
def builds_for_recipe(recipe): |
345 |
"""A list of interesting builds.
|
|
346 |
||
347 |
All pending builds are shown, as well as 1-5 recent builds.
|
|
348 |
Recent builds are ordered by date finished (if completed) or
|
|
349 |
date_started (if date finished is not set due to an error building or
|
|
350 |
other circumstance which resulted in the build not being completed).
|
|
351 |
This allows started but unfinished builds to show up in the view but
|
|
352 |
be discarded as more recent builds become available.
|
|
14625.1.1
by Steve Kowalik
Hide SPRBs that the user can't see in the SPRecipe views. |
353 |
|
354 |
Builds that the user does not have permission to see are excluded.
|
|
12013.4.6
by Ian Booth
Hook up validation errors |
355 |
"""
|
14625.1.1
by Steve Kowalik
Hide SPRBs that the user can't see in the SPRecipe views. |
356 |
builds = [build for build in recipe.pending_builds |
357 |
if check_permission('launchpad.View', build)] |
|
12397.2.8
by Ian Booth
Change from using getter methods to properties for exported recipe and build accessors |
358 |
for build in recipe.completed_builds: |
14625.1.1
by Steve Kowalik
Hide SPRBs that the user can't see in the SPRecipe views. |
359 |
if not check_permission('launchpad.View', build): |
360 |
continue
|
|
12013.4.6
by Ian Booth
Hook up validation errors |
361 |
builds.append(build) |
362 |
if len(builds) >= 5: |
|
363 |
break
|
|
364 |
return builds |
|
365 |
||
366 |
||
12378.3.13
by Ian Booth
Fix typo in test |
367 |
def new_builds_notification_text(builds, already_pending=None): |
12378.2.11
by Ian Booth
Code review changes plus add new build notification for non-ajax version |
368 |
nr_builds = len(builds) |
12378.2.17
by Ian Booth
Add info message for ajax build now requests |
369 |
if not nr_builds: |
370 |
builds_text = "All requested recipe builds are already queued." |
|
371 |
elif nr_builds == 1: |
|
372 |
builds_text = "1 new recipe build has been queued." |
|
12378.2.11
by Ian Booth
Code review changes plus add new build notification for non-ajax version |
373 |
else: |
12378.2.17
by Ian Booth
Add info message for ajax build now requests |
374 |
builds_text = "%d new recipe builds have been queued." % nr_builds |
12378.3.5
by Ian Booth
Better informational/error message display plus remove dup code |
375 |
if nr_builds > 0 and already_pending: |
376 |
builds_text = "<p>%s</p>%s" % (builds_text, already_pending) |
|
377 |
return structured(builds_text) |
|
12378.2.11
by Ian Booth
Code review changes plus add new build notification for non-ajax version |
378 |
|
379 |
||
10498.5.1
by Aaron Bentley
Get distroseries listing displaying. |
380 |
class SourcePackageRecipeRequestBuildsView(LaunchpadFormView): |
381 |
"""A view for requesting builds of a SourcePackageRecipe."""
|
|
382 |
||
383 |
@property
|
|
384 |
def initial_values(self): |
|
10498.5.16
by Aaron Bentley
Update docs |
385 |
"""Set initial values for the widgets.
|
386 |
||
387 |
The distroseries function as defaults for requesting a build.
|
|
388 |
"""
|
|
12547.1.36
by Ian Booth
Test fixes |
389 |
initial_values = {'distroseries': self.context.distroseries} |
12397.2.8
by Ian Booth
Change from using getter methods to properties for exported recipe and build accessors |
390 |
build = self.context.last_build |
14625.1.1
by Steve Kowalik
Hide SPRBs that the user can't see in the SPRecipe views. |
391 |
if build: |
14625.1.2
by Steve Kowalik
Shift the check_permission() call under if build. |
392 |
# If the build can't be viewed, the archive can't.
|
393 |
if check_permission('launchpad.View', build): |
|
394 |
initial_values['archive'] = build.archive |
|
10875.2.1
by Aaron Bentley
Use last build archive as initial value |
395 |
return initial_values |
10498.7.1
by Aaron Bentley
Require each distroseries to have at least one enabled architecture. |
396 |
|
10498.5.19
by Aaron Bentley
Convert schema to use vocabulary utilities. |
397 |
class schema(Interface): |
398 |
"""Schema for requesting a build."""
|
|
12013.4.2
by Ian Booth
Initial implementation |
399 |
archive = Choice(vocabulary='TargetPPAs', title=u'Archive') |
12547.1.2
by Ian Booth
Add inline distro series editing - first cut |
400 |
distroseries = List( |
10498.5.19
by Aaron Bentley
Convert schema to use vocabulary utilities. |
401 |
Choice(vocabulary='BuildableDistroSeries'), |
402 |
title=u'Distribution series') |
|
10498.5.1
by Aaron Bentley
Get distroseries listing displaying. |
403 |
|
12547.1.2
by Ian Booth
Add inline distro series editing - first cut |
404 |
custom_widget('distroseries', LabeledMultiCheckBoxWidget) |
10498.5.1
by Aaron Bentley
Get distroseries listing displaying. |
405 |
|
10936.6.2
by Aaron Bentley
Handle over-quota builds |
406 |
def validate(self, data): |
12547.1.2
by Ian Booth
Add inline distro series editing - first cut |
407 |
distros = data.get('distroseries', []) |
12013.4.3
by Ian Booth
Factor out sepatate view for builds table aned update from ajax call |
408 |
if not len(distros): |
12547.1.2
by Ian Booth
Add inline distro series editing - first cut |
409 |
self.setFieldError('distroseries', |
12013.4.6
by Ian Booth
Hook up validation errors |
410 |
"You need to specify at least one distro series for which "
|
411 |
"to build.") |
|
12013.4.3
by Ian Booth
Factor out sepatate view for builds table aned update from ajax call |
412 |
return
|
12013.4.6
by Ian Booth
Hook up validation errors |
413 |
over_quota_distroseries = [] |
12547.1.2
by Ian Booth
Add inline distro series editing - first cut |
414 |
for distroseries in data['distroseries']: |
10936.6.2
by Aaron Bentley
Handle over-quota builds |
415 |
if self.context.isOverQuota(self.user, distroseries): |
416 |
over_quota_distroseries.append(str(distroseries)) |
|
417 |
if len(over_quota_distroseries) > 0: |
|
418 |
self.setFieldError( |
|
12547.1.2
by Ian Booth
Add inline distro series editing - first cut |
419 |
'distroseries', |
10936.6.2
by Aaron Bentley
Handle over-quota builds |
420 |
"You have exceeded today's quota for %s." % |
421 |
', '.join(over_quota_distroseries)) |
|
422 |
||
12013.4.6
by Ian Booth
Hook up validation errors |
423 |
def requestBuild(self, data): |
12013.4.15
by Ian Booth
Code review fixes |
424 |
"""User action for requesting a number of builds.
|
425 |
||
426 |
We raise exceptions for most errors but if there's already a pending
|
|
427 |
build for a particular distroseries, we simply record that so that
|
|
428 |
other builds can ne queued and a message be displayed to the caller.
|
|
429 |
"""
|
|
12378.3.5
by Ian Booth
Better informational/error message display plus remove dup code |
430 |
informational = {} |
12378.2.11
by Ian Booth
Code review changes plus add new build notification for non-ajax version |
431 |
builds = [] |
12547.1.2
by Ian Booth
Add inline distro series editing - first cut |
432 |
for distroseries in data['distroseries']: |
7675.729.3
by Aaron Bentley
Handle identical builds in the UI. |
433 |
try: |
12378.2.11
by Ian Booth
Code review changes plus add new build notification for non-ajax version |
434 |
build = self.context.requestBuild( |
12013.4.2
by Ian Booth
Initial implementation |
435 |
data['archive'], self.user, distroseries, manual=True) |
12378.2.11
by Ian Booth
Code review changes plus add new build notification for non-ajax version |
436 |
builds.append(build) |
7675.729.3
by Aaron Bentley
Handle identical builds in the UI. |
437 |
except BuildAlreadyPending, e: |
12378.3.5
by Ian Booth
Better informational/error message display plus remove dup code |
438 |
existing_message = informational.get("already_pending") |
439 |
if existing_message: |
|
440 |
new_message = existing_message[:-1] + ( |
|
12378.3.1
by Ian Booth
Add code to display partial request build successes |
441 |
", and %s." % e.distroseries) |
442 |
else: |
|
12378.3.5
by Ian Booth
Better informational/error message display plus remove dup code |
443 |
new_message = ("An identical build is " |
12378.3.1
by Ian Booth
Add code to display partial request build successes |
444 |
"already pending for %s." % e.distroseries) |
12378.3.5
by Ian Booth
Better informational/error message display plus remove dup code |
445 |
informational["already_pending"] = new_message |
12378.3.7
by Ian Booth
Add windmill test |
446 |
|
12378.3.5
by Ian Booth
Better informational/error message display plus remove dup code |
447 |
return builds, informational |
12013.4.6
by Ian Booth
Hook up validation errors |
448 |
|
12013.4.10
by Ian Booth
Lint issues |
449 |
|
12013.4.6
by Ian Booth
Hook up validation errors |
450 |
class SourcePackageRecipeRequestBuildsHtmlView( |
451 |
SourcePackageRecipeRequestBuildsView): |
|
452 |
"""Supports HTML form recipe build requests."""
|
|
453 |
||
454 |
@property
|
|
455 |
def title(self): |
|
456 |
return 'Request builds for %s' % self.context.name |
|
457 |
||
458 |
label = title |
|
459 |
||
460 |
@property
|
|
461 |
def cancel_url(self): |
|
462 |
return canonical_url(self.context) |
|
463 |
||
464 |
@action('Request builds', name='request') |
|
465 |
def request_action(self, action, data): |
|
12378.3.5
by Ian Booth
Better informational/error message display plus remove dup code |
466 |
builds, informational = self.requestBuild(data) |
12013.4.6
by Ian Booth
Hook up validation errors |
467 |
self.next_url = self.cancel_url |
12378.3.5
by Ian Booth
Better informational/error message display plus remove dup code |
468 |
already_pending = informational.get("already_pending") |
469 |
notification_text = new_builds_notification_text( |
|
470 |
builds, already_pending) |
|
471 |
self.request.response.addNotification(notification_text) |
|
12013.4.6
by Ian Booth
Hook up validation errors |
472 |
|
473 |
||
474 |
class SourcePackageRecipeRequestBuildsAjaxView( |
|
475 |
SourcePackageRecipeRequestBuildsView): |
|
476 |
"""Supports AJAX form recipe build requests."""
|
|
477 |
||
12378.3.5
by Ian Booth
Better informational/error message display plus remove dup code |
478 |
def _process_error(self, data=None, builds=None, informational=None, |
479 |
errors=None, reason="Validation"): |
|
12013.4.15
by Ian Booth
Code review fixes |
480 |
"""Set up the response and json data to return to the caller."""
|
13662.7.93
by Henning Eggers
Refactored request build. |
481 |
self.request.response.setStatus(200, reason) |
12013.4.6
by Ian Booth
Hook up validation errors |
482 |
self.request.response.setHeader('Content-type', 'application/json') |
12378.3.5
by Ian Booth
Better informational/error message display plus remove dup code |
483 |
return_data = dict(builds=builds, errors=errors) |
484 |
if informational: |
|
485 |
return_data.update(informational) |
|
486 |
return simplejson.dumps(return_data) |
|
12013.4.6
by Ian Booth
Hook up validation errors |
487 |
|
488 |
def failure(self, action, data, errors): |
|
12013.4.15
by Ian Booth
Code review fixes |
489 |
"""Called by the form if validate() finds any errors.
|
490 |
||
491 |
We simply convert the errors to json and return that data to the
|
|
492 |
caller for display to the user.
|
|
493 |
"""
|
|
12378.3.5
by Ian Booth
Better informational/error message display plus remove dup code |
494 |
return self._process_error(data=data, errors=self.widget_errors) |
12013.4.6
by Ian Booth
Hook up validation errors |
495 |
|
496 |
@action('Request builds', name='request', failure=failure) |
|
497 |
def request_action(self, action, data): |
|
12013.4.15
by Ian Booth
Code review fixes |
498 |
"""User action for requesting a number of builds.
|
499 |
||
500 |
The failure handler will handle any validation errors. We still need
|
|
501 |
to handle errors which may occur when invoking the business logic.
|
|
502 |
These "expected" errors are ones which result in a predefined message
|
|
503 |
being displayed to the user. If the business method raises an
|
|
504 |
unexpected exception, that will be handled using the form's standard
|
|
505 |
exception processing mechanism (using response code 500).
|
|
506 |
"""
|
|
12378.3.5
by Ian Booth
Better informational/error message display plus remove dup code |
507 |
builds, informational = self.requestBuild(data) |
12013.4.8
by Ian Booth
Add windmill tests |
508 |
# If there are errors we return a json data snippet containing the
|
12378.3.5
by Ian Booth
Better informational/error message display plus remove dup code |
509 |
# errors as well as the form content. These errors are processed
|
510 |
# by the caller's response handler and displayed to the user. The
|
|
511 |
# form content may be rendered as well if required.
|
|
512 |
if informational: |
|
513 |
builds_html = None |
|
514 |
if len(builds): |
|
515 |
builds_html = self.render() |
|
516 |
return self._process_error( |
|
517 |
data=data, builds=builds_html, informational=informational, |
|
518 |
reason="Request Build") |
|
12013.4.6
by Ian Booth
Hook up validation errors |
519 |
|
520 |
@property
|
|
521 |
def builds(self): |
|
522 |
return builds_for_recipe(self.context) |
|
7675.729.3
by Aaron Bentley
Handle identical builds in the UI. |
523 |
|
10795.6.13
by Aaron Bentley
Show binary builds resulting from sourcepackagebuild. |
524 |
|
12378.2.5
by Ian Booth
Add tests and support for non-ajax forms |
525 |
class SourcePackageRecipeRequestDailyBuildView(LaunchpadFormView): |
12378.2.3
by Ian Booth
Initial implementation |
526 |
"""Supports requests to perform a daily build for a recipe.
|
527 |
||
528 |
Renders the recipe builds table so that the recipe index page can be
|
|
529 |
updated with the new build records.
|
|
12378.2.5
by Ian Booth
Add tests and support for non-ajax forms |
530 |
|
531 |
This view works for both ajax and html form requests.
|
|
12378.2.3
by Ian Booth
Initial implementation |
532 |
"""
|
533 |
||
12378.2.5
by Ian Booth
Add tests and support for non-ajax forms |
534 |
# Attributes for the html version
|
12378.2.11
by Ian Booth
Code review changes plus add new build notification for non-ajax version |
535 |
page_title = "Build now" |
12378.2.5
by Ian Booth
Add tests and support for non-ajax forms |
536 |
|
537 |
class schema(Interface): |
|
538 |
"""Schema for requesting a build."""
|
|
539 |
||
540 |
@action('Build now', name='build') |
|
541 |
def build_action(self, action, data): |
|
12378.2.3
by Ian Booth
Initial implementation |
542 |
recipe = self.context |
13824.2.1
by Brad Crittenden
Catch TooManyBuild exception rather than OOPS |
543 |
try: |
544 |
builds = recipe.performDailyBuild() |
|
13912.4.2
by Aaron Bentley
Handle ArchiveDisabled as user error. |
545 |
except (TooManyBuilds, ArchiveDisabled) as e: |
13824.2.1
by Brad Crittenden
Catch TooManyBuild exception rather than OOPS |
546 |
self.request.response.addErrorNotification(str(e)) |
547 |
self.next_url = canonical_url(recipe) |
|
548 |
return
|
|
549 |
||
12378.2.5
by Ian Booth
Add tests and support for non-ajax forms |
550 |
if self.request.is_ajax: |
551 |
template = ViewPageTemplateFile( |
|
552 |
"../templates/sourcepackagerecipe-builds.pt") |
|
553 |
return template(self) |
|
554 |
else: |
|
555 |
self.next_url = canonical_url(recipe) |
|
12378.2.11
by Ian Booth
Code review changes plus add new build notification for non-ajax version |
556 |
self.request.response.addNotification( |
557 |
new_builds_notification_text(builds)) |
|
12378.2.3
by Ian Booth
Initial implementation |
558 |
|
559 |
@property
|
|
560 |
def builds(self): |
|
561 |
return builds_for_recipe(self.context) |
|
562 |
||
563 |
||
11929.11.17
by Tim Penhey
Rework the new page to allow the creation of a ppa on the fly. |
564 |
class ISourcePackageEditSchema(Interface): |
7675.618.46
by Paul Hummer
Fixed indentation |
565 |
"""Schema for adding or editing a recipe."""
|
7675.618.41
by Paul Hummer
Got a working edit |
566 |
|
7675.618.46
by Paul Hummer
Fixed indentation |
567 |
use_template(ISourcePackageRecipe, include=[ |
568 |
'name', |
|
569 |
'description', |
|
570 |
'owner', |
|
10409.5.105
by Curtis Hovey
Merged devel, resolved conflicts. |
571 |
'build_daily', |
12547.1.7
by Ian Booth
Refactor spr interface and improve rendering |
572 |
'distroseries', |
7675.618.46
by Paul Hummer
Fixed indentation |
573 |
])
|
10899.4.1
by Aaron Bentley
Initial UI for source package recipe builds. |
574 |
daily_build_archive = Choice(vocabulary='TargetPPAs', |
11862.1.4
by Paul Hummer
Fixed some missing descriptions |
575 |
title=u'Daily build archive', |
576 |
description=( |
|
11862.1.5
by Paul Hummer
Made deryck's suggested change |
577 |
u'If built daily, this is the archive where the package ' |
11862.1.4
by Paul Hummer
Fixed some missing descriptions |
578 |
u'will be uploaded.')) |
11929.11.6
by Tim Penhey
Allow forms to specify that some fields have structured widget help. |
579 |
recipe_text = has_structured_doc( |
580 |
Text( |
|
581 |
title=u'Recipe text', required=True, |
|
14354.1.1
by Francis J. Lacoste
Revert sourcerecipe help link change. |
582 |
description=u"""The text of the recipe. |
14388.1.5
by William Grant
Use new help directories. |
583 |
<a href="/+help-code/recipe-syntax.html" target="help"
|
14354.1.1
by Francis J. Lacoste
Revert sourcerecipe help link change. |
584 |
>Syntax help
|
585 |
<span class="sprite maybe">
|
|
586 |
<span class="invisible-link">Help</span>
|
|
587 |
</span></a>
|
|
11929.11.6
by Tim Penhey
Allow forms to specify that some fields have structured widget help. |
588 |
""")) |
7675.618.41
by Paul Hummer
Got a working edit |
589 |
|
7675.622.1
by Paul Hummer
Fixing MOST of the concerns in Tim's review (still need to make LaunchpadEditForm work |
590 |
|
11929.11.17
by Tim Penhey
Rework the new page to allow the creation of a ppa on the fly. |
591 |
EXISTING_PPA = 'existing-ppa' |
592 |
CREATE_NEW = 'create-new' |
|
593 |
||
594 |
||
595 |
USE_ARCHIVE_VOCABULARY = SimpleVocabulary(( |
|
11929.11.28
by Tim Penhey
Other tweaks suggested by reviewer. |
596 |
SimpleTerm(EXISTING_PPA, EXISTING_PPA, _("Use an existing PPA")), |
597 |
SimpleTerm( |
|
598 |
CREATE_NEW, CREATE_NEW, _("Create a new PPA for this recipe")), |
|
11929.11.17
by Tim Penhey
Rework the new page to allow the creation of a ppa on the fly. |
599 |
))
|
600 |
||
601 |
||
602 |
class ISourcePackageAddSchema(ISourcePackageEditSchema): |
|
603 |
||
11929.11.26
by Tim Penhey
Default the initial ppa name to 'ppa' if the user has no existing PPAs. |
604 |
daily_build_archive = Choice(vocabulary='TargetPPAs', |
605 |
title=u'Daily build archive', required=False, |
|
606 |
description=( |
|
607 |
u'If built daily, this is the archive where the package ' |
|
608 |
u'will be uploaded.')) |
|
609 |
||
11929.11.17
by Tim Penhey
Rework the new page to allow the creation of a ppa on the fly. |
610 |
use_ppa = Choice( |
611 |
title=_('Which PPA'), |
|
612 |
vocabulary=USE_ARCHIVE_VOCABULARY, |
|
613 |
description=_("Which PPA to use..."), |
|
614 |
required=True) |
|
615 |
||
616 |
ppa_name = TextLine( |
|
617 |
title=_("New PPA name"), required=False, |
|
618 |
constraint=name_validator, |
|
11929.11.24
by Tim Penhey
Tweak the description text. |
619 |
description=_("A new PPA with this name will be created for " |
620 |
"the owner of the recipe .")) |
|
11929.11.17
by Tim Penhey
Rework the new page to allow the creation of a ppa on the fly. |
621 |
|
12335.5.4
by Steve Kowalik
Lint fixes |
622 |
|
12335.5.2
by Steve Kowalik
Use a new exception to show that an error was raised, and update the two |
623 |
class ErrorHandled(Exception): |
624 |
"""A field error occured and was handled."""
|
|
625 |
||
11929.11.17
by Tim Penhey
Rework the new page to allow the creation of a ppa on the fly. |
626 |
|
7675.622.1
by Paul Hummer
Fixing MOST of the concerns in Tim's review (still need to make LaunchpadEditForm work |
627 |
class RecipeTextValidatorMixin: |
628 |
"""Class to validate that the Source Package Recipe text is valid."""
|
|
629 |
||
630 |
def validate(self, data): |
|
10899.4.1
by Aaron Bentley
Initial UI for source package recipe builds. |
631 |
if data['build_daily']: |
12547.1.2
by Ian Booth
Add inline distro series editing - first cut |
632 |
if len(data['distroseries']) == 0: |
10899.4.1
by Aaron Bentley
Initial UI for source package recipe builds. |
633 |
self.setFieldError( |
12547.1.2
by Ian Booth
Add inline distro series editing - first cut |
634 |
'distroseries', |
10899.4.1
by Aaron Bentley
Initial UI for source package recipe builds. |
635 |
'You must specify at least one series for daily builds.') |
7675.622.1
by Paul Hummer
Fixing MOST of the concerns in Tim's review (still need to make LaunchpadEditForm work |
636 |
try: |
637 |
parser = RecipeParser(data['recipe_text']) |
|
10788.2.2
by Aaron Bentley
Fix lint errors. |
638 |
parser.parse() |
11151.1.1
by Paul Hummer
Pushing the bzr-builder recipe out to the user. |
639 |
except RecipeParseError, error: |
11970.1.1
by Aaron Bentley
Show usage for intruction parse errors. |
640 |
self.setFieldError('recipe_text', str(error)) |
7675.622.1
by Paul Hummer
Fixing MOST of the concerns in Tim's review (still need to make LaunchpadEditForm work |
641 |
|
12335.5.2
by Steve Kowalik
Use a new exception to show that an error was raised, and update the two |
642 |
def error_handler(self, callable, *args, **kwargs): |
12335.5.1
by Steve Kowalik
* Refactor the error handling for creating and updating a recipe into |
643 |
try: |
12335.5.2
by Steve Kowalik
Use a new exception to show that an error was raised, and update the two |
644 |
return callable(*args) |
12335.5.1
by Steve Kowalik
* Refactor the error handling for creating and updating a recipe into |
645 |
except TooNewRecipeFormat: |
646 |
self.setFieldError( |
|
647 |
'recipe_text', |
|
648 |
'The recipe format version specified is not available.') |
|
649 |
except ForbiddenInstructionError, e: |
|
650 |
self.setFieldError( |
|
651 |
'recipe_text', |
|
652 |
'The bzr-builder instruction "%s" is not permitted ' |
|
653 |
'here.' % e.instruction_name) |
|
654 |
except NoSuchBranch, e: |
|
655 |
self.setFieldError( |
|
656 |
'recipe_text', '%s is not a branch on Launchpad.' % e.name) |
|
657 |
except PrivateBranchRecipe, e: |
|
658 |
self.setFieldError('recipe_text', str(e)) |
|
12335.5.2
by Steve Kowalik
Use a new exception to show that an error was raised, and update the two |
659 |
raise ErrorHandled() |
660 |
||
7675.622.1
by Paul Hummer
Fixing MOST of the concerns in Tim's review (still need to make LaunchpadEditForm work |
661 |
|
11994.2.16
by Ian Booth
Add new tests and code to allow for branchesrelated to source packages as well as products |
662 |
class RelatedBranchesWidget(Widget): |
663 |
"""A widget to render the related branches for a recipe."""
|
|
664 |
implements(IView) |
|
665 |
||
666 |
__call__ = ViewPageTemplateFile( |
|
667 |
'../templates/sourcepackagerecipe-related-branches.pt') |
|
668 |
||
11994.2.21
by Ian Booth
Refactor related branches methods to IBranchTarget and show distro series instead of source package for related package branches |
669 |
related_package_branch_info = [] |
11994.2.19
by Ian Booth
Make ui pretty, keep private branches hidden, display series instead of owner, new tests |
670 |
related_series_branch_info = [] |
11994.2.16
by Ian Booth
Add new tests and code to allow for branchesrelated to source packages as well as products |
671 |
|
672 |
def hasInput(self): |
|
673 |
return True |
|
674 |
||
675 |
def setRenderedValue(self, value): |
|
11994.2.21
by Ian Booth
Refactor related branches methods to IBranchTarget and show distro series instead of source package for related package branches |
676 |
self.related_package_branch_info = ( |
677 |
value['related_package_branch_info']) |
|
11994.2.19
by Ian Booth
Make ui pretty, keep private branches hidden, display series instead of owner, new tests |
678 |
self.related_series_branch_info = value['related_series_branch_info'] |
679 |
||
680 |
||
11994.2.21
by Ian Booth
Refactor related branches methods to IBranchTarget and show distro series instead of source package for related package branches |
681 |
class RecipeRelatedBranchesMixin(LaunchpadFormView): |
11994.2.16
by Ian Booth
Add new tests and code to allow for branchesrelated to source packages as well as products |
682 |
"""A class to find related branches for a recipe's base branch."""
|
683 |
||
684 |
custom_widget('related-branches', RelatedBranchesWidget) |
|
685 |
||
686 |
def extendFields(self): |
|
687 |
"""See `LaunchpadFormView`.
|
|
688 |
||
689 |
Adds a related branches field to the form.
|
|
690 |
"""
|
|
691 |
self.form_fields += form.Fields(Field(__name__='related-branches')) |
|
692 |
self.form_fields['related-branches'].custom_widget = ( |
|
693 |
self.custom_widgets['related-branches']) |
|
694 |
self.widget_errors['related-branches'] = '' |
|
695 |
||
696 |
def setUpWidgets(self, context=None): |
|
697 |
# Adds a new related branches widget.
|
|
698 |
super(RecipeRelatedBranchesMixin, self).setUpWidgets(context) |
|
699 |
self.widgets['related-branches'].display_label = False |
|
700 |
self.widgets['related-branches'].setRenderedValue(dict( |
|
11994.2.21
by Ian Booth
Refactor related branches methods to IBranchTarget and show distro series instead of source package for related package branches |
701 |
related_package_branch_info=self.related_package_branch_info, |
11994.2.19
by Ian Booth
Make ui pretty, keep private branches hidden, display series instead of owner, new tests |
702 |
related_series_branch_info=self.related_series_branch_info)) |
703 |
||
11994.2.16
by Ian Booth
Add new tests and code to allow for branchesrelated to source packages as well as products |
704 |
@cachedproperty
|
11994.2.19
by Ian Booth
Make ui pretty, keep private branches hidden, display series instead of owner, new tests |
705 |
def related_series_branch_info(self): |
11994.2.16
by Ian Booth
Add new tests and code to allow for branchesrelated to source packages as well as products |
706 |
branch_to_check = self.getBranch() |
11994.2.21
by Ian Booth
Refactor related branches methods to IBranchTarget and show distro series instead of source package for related package branches |
707 |
return IBranchTarget( |
708 |
branch_to_check.target).getRelatedSeriesBranchInfo( |
|
11994.2.24
by Ian Booth
Add option to limit the related branches results. UI only displays top 5 most recent branches |
709 |
branch_to_check, |
710 |
limit_results=5) |
|
11994.2.16
by Ian Booth
Add new tests and code to allow for branchesrelated to source packages as well as products |
711 |
|
712 |
@cachedproperty
|
|
11994.2.21
by Ian Booth
Refactor related branches methods to IBranchTarget and show distro series instead of source package for related package branches |
713 |
def related_package_branch_info(self): |
11994.2.16
by Ian Booth
Add new tests and code to allow for branchesrelated to source packages as well as products |
714 |
branch_to_check = self.getBranch() |
11994.2.21
by Ian Booth
Refactor related branches methods to IBranchTarget and show distro series instead of source package for related package branches |
715 |
return IBranchTarget( |
716 |
branch_to_check.target).getRelatedPackageBranchInfo( |
|
11994.2.24
by Ian Booth
Add option to limit the related branches results. UI only displays top 5 most recent branches |
717 |
branch_to_check, |
718 |
limit_results=5) |
|
11994.2.16
by Ian Booth
Add new tests and code to allow for branchesrelated to source packages as well as products |
719 |
|
720 |
||
721 |
class SourcePackageRecipeAddView(RecipeRelatedBranchesMixin, |
|
722 |
RecipeTextValidatorMixin, LaunchpadFormView): |
|
7675.618.41
by Paul Hummer
Got a working edit |
723 |
"""View for creating Source Package Recipes."""
|
724 |
||
725 |
title = label = 'Create a new source package recipe' |
|
726 |
||
11929.11.17
by Tim Penhey
Rework the new page to allow the creation of a ppa on the fly. |
727 |
schema = ISourcePackageAddSchema |
12547.1.2
by Ian Booth
Add inline distro series editing - first cut |
728 |
custom_widget('distroseries', LabeledMultiCheckBoxWidget) |
11869.12.1
by Aaron Bentley
Recipe owner proof-of-concept. |
729 |
custom_widget('owner', RecipeOwnerWidget) |
11929.11.17
by Tim Penhey
Rework the new page to allow the creation of a ppa on the fly. |
730 |
custom_widget('use_ppa', LaunchpadRadioWidget) |
10744.5.3
by Paul Hummer
Got a working create recipe flow |
731 |
|
11049.5.2
by Paul Hummer
Added Warning notification |
732 |
def initialize(self): |
733 |
super(SourcePackageRecipeAddView, self).initialize() |
|
11929.11.17
by Tim Penhey
Rework the new page to allow the creation of a ppa on the fly. |
734 |
widget = self.widgets['use_ppa'] |
735 |
current_value = widget._getFormValue() |
|
736 |
self.use_ppa_existing = render_radio_widget_part( |
|
737 |
widget, EXISTING_PPA, current_value) |
|
738 |
self.use_ppa_new = render_radio_widget_part( |
|
739 |
widget, CREATE_NEW, current_value) |
|
11929.11.26
by Tim Penhey
Default the initial ppa name to 'ppa' if the user has no existing PPAs. |
740 |
archive_widget = self.widgets['daily_build_archive'] |
741 |
self.show_ppa_chooser = len(archive_widget.vocabulary) > 0 |
|
742 |
if not self.show_ppa_chooser: |
|
743 |
self.widgets['ppa_name'].setRenderedValue('ppa') |
|
744 |
# Force there to be no '(no value)' item in the select. We do this as
|
|
745 |
# the input isn't listed as 'required' otherwise the validator gets
|
|
746 |
# all confused when we want to create a new PPA.
|
|
747 |
archive_widget._displayItemForMissingValue = False |
|
11929.11.17
by Tim Penhey
Rework the new page to allow the creation of a ppa on the fly. |
748 |
|
12547.1.36
by Ian Booth
Test fixes |
749 |
def setUpFields(self): |
750 |
super(SourcePackageRecipeAddView, self).setUpFields() |
|
751 |
# Ensure distro series widget allows input
|
|
752 |
self.form_fields['distroseries'].for_input = True |
|
753 |
||
11994.2.16
by Ian Booth
Add new tests and code to allow for branchesrelated to source packages as well as products |
754 |
def getBranch(self): |
755 |
"""The branch on which the recipe is built."""
|
|
756 |
return self.context |
|
757 |
||
12373.2.1
by Tim Penhey
First hack. |
758 |
def _recipe_names(self): |
759 |
"""A generator of recipe names."""
|
|
12599.4.2
by Leonard Richardson
Merge from trunk. |
760 |
# +junk-daily doesn't make a very good recipe name, so use the
|
761 |
# branch name in that case.
|
|
762 |
if zope_isinstance(self.context.target, PersonBranchTarget): |
|
763 |
branch_target_name = self.context.name |
|
764 |
else: |
|
765 |
branch_target_name = self.context.target.name.split('/')[-1] |
|
12373.2.6
by Tim Penhey
Fix the typo, and update the description for the name. |
766 |
yield "%s-daily" % branch_target_name |
12373.2.1
by Tim Penhey
First hack. |
767 |
counter = itertools.count(1) |
768 |
while True: |
|
769 |
yield "%s-daily-%s" % (branch_target_name, counter.next()) |
|
770 |
||
771 |
def _find_unused_name(self, owner): |
|
772 |
# Grab the last path element of the branch target path.
|
|
773 |
source = getUtility(ISourcePackageRecipeSource) |
|
774 |
for recipe_name in self._recipe_names(): |
|
12378.3.2
by Ian Booth
Lint |
775 |
if not source.exists(owner, recipe_name): |
776 |
return recipe_name |
|
12373.2.1
by Tim Penhey
First hack. |
777 |
|
10744.5.3
by Paul Hummer
Got a working create recipe flow |
778 |
@property
|
779 |
def initial_values(self): |
|
12373.2.16
by Tim Penhey
Rename to distroseries. |
780 |
distroseries = get_buildable_distroseries_set(self.user) |
781 |
series = [series for series in distroseries if series.status in ( |
|
12373.2.8
by Tim Penhey
Set initial distroseries, and make description required again (needs db patch to remove not null constraint). |
782 |
SeriesStatus.CURRENT, SeriesStatus.DEVELOPMENT)] |
7675.618.43
by Paul Hummer
Added owner to the add/edit form |
783 |
return { |
12378.3.2
by Ian Booth
Lint |
784 |
'name': self._find_unused_name(self.user), |
7675.618.43
by Paul Hummer
Added owner to the add/edit form |
785 |
'recipe_text': MINIMAL_RECIPE_TEXT % self.context.bzr_identity, |
10899.4.1
by Aaron Bentley
Initial UI for source package recipe builds. |
786 |
'owner': self.user, |
12547.1.2
by Ian Booth
Add inline distro series editing - first cut |
787 |
'distroseries': series, |
12373.2.8
by Tim Penhey
Set initial distroseries, and make description required again (needs db patch to remove not null constraint). |
788 |
'build_daily': True, |
11929.11.17
by Tim Penhey
Rework the new page to allow the creation of a ppa on the fly. |
789 |
'use_ppa': EXISTING_PPA, |
790 |
}
|
|
10744.5.3
by Paul Hummer
Got a working create recipe flow |
791 |
|
792 |
@property
|
|
793 |
def cancel_url(self): |
|
794 |
return canonical_url(self.context) |
|
795 |
||
7675.622.1
by Paul Hummer
Fixing MOST of the concerns in Tim's review (still need to make LaunchpadEditForm work |
796 |
@action('Create Recipe', name='create') |
10744.5.3
by Paul Hummer
Got a working create recipe flow |
797 |
def request_action(self, action, data): |
12335.5.1
by Steve Kowalik
* Refactor the error handling for creating and updating a recipe into |
798 |
owner = data['owner'] |
799 |
if data['use_ppa'] == CREATE_NEW: |
|
800 |
ppa_name = data.get('ppa_name', None) |
|
801 |
ppa = owner.createPPA(ppa_name) |
|
802 |
else: |
|
803 |
ppa = data['daily_build_archive'] |
|
12335.5.2
by Steve Kowalik
Use a new exception to show that an error was raised, and update the two |
804 |
try: |
12335.5.5
by Steve Kowalik
super() isn't needed, switch to self. |
805 |
source_package_recipe = self.error_handler( |
806 |
getUtility(ISourcePackageRecipeSource).new, |
|
807 |
self.user, owner, data['name'], |
|
12547.1.21
by Ian Booth
Lint |
808 |
data['recipe_text'], data['description'], |
809 |
data['distroseries'], ppa, data['build_daily']) |
|
12335.5.2
by Steve Kowalik
Use a new exception to show that an error was raised, and update the two |
810 |
Store.of(source_package_recipe).flush() |
811 |
except ErrorHandled: |
|
12335.5.1
by Steve Kowalik
* Refactor the error handling for creating and updating a recipe into |
812 |
return
|
10979.1.3
by Paul Hummer
Fixed the failing test |
813 |
|
10744.5.3
by Paul Hummer
Got a working create recipe flow |
814 |
self.next_url = canonical_url(source_package_recipe) |
7675.618.39
by Paul Hummer
Added a recipe edit view. |
815 |
|
7675.711.9
by Paul Hummer
Fixed the test (and the bug) |
816 |
def validate(self, data): |
817 |
super(SourcePackageRecipeAddView, self).validate(data) |
|
818 |
name = data.get('name', None) |
|
7675.711.12
by Paul Hummer
Fixed a potential bug |
819 |
owner = data.get('owner', None) |
820 |
if name and owner: |
|
7675.711.9
by Paul Hummer
Fixed the test (and the bug) |
821 |
SourcePackageRecipeSource = getUtility(ISourcePackageRecipeSource) |
7675.711.12
by Paul Hummer
Fixed a potential bug |
822 |
if SourcePackageRecipeSource.exists(owner, name): |
7675.711.9
by Paul Hummer
Fixed the test (and the bug) |
823 |
self.setFieldError( |
824 |
'name', |
|
825 |
'There is already a recipe owned by %s with this name.' % |
|
7675.711.12
by Paul Hummer
Fixed a potential bug |
826 |
owner.displayname) |
11929.11.17
by Tim Penhey
Rework the new page to allow the creation of a ppa on the fly. |
827 |
if data['use_ppa'] == CREATE_NEW: |
828 |
ppa_name = data.get('ppa_name', None) |
|
11929.11.26
by Tim Penhey
Default the initial ppa name to 'ppa' if the user has no existing PPAs. |
829 |
if ppa_name is None: |
830 |
self.setFieldError( |
|
831 |
'ppa_name', 'You need to specify a name for the PPA.') |
|
832 |
else: |
|
833 |
error = Archive.validatePPA(owner, ppa_name) |
|
834 |
if error is not None: |
|
835 |
self.setFieldError('ppa_name', error) |
|
7675.711.9
by Paul Hummer
Fixed the test (and the bug) |
836 |
|
7675.618.39
by Paul Hummer
Added a recipe edit view. |
837 |
|
11994.2.16
by Ian Booth
Add new tests and code to allow for branchesrelated to source packages as well as products |
838 |
class SourcePackageRecipeEditView(RecipeRelatedBranchesMixin, |
839 |
RecipeTextValidatorMixin, |
|
7675.622.1
by Paul Hummer
Fixing MOST of the concerns in Tim's review (still need to make LaunchpadEditForm work |
840 |
LaunchpadEditFormView): |
841 |
"""View for editing Source Package Recipes."""
|
|
7675.618.39
by Paul Hummer
Added a recipe edit view. |
842 |
|
11994.2.16
by Ian Booth
Add new tests and code to allow for branchesrelated to source packages as well as products |
843 |
def getBranch(self): |
844 |
"""The branch on which the recipe is built."""
|
|
845 |
return self.context.base_branch |
|
846 |
||
7675.618.39
by Paul Hummer
Added a recipe edit view. |
847 |
@property
|
848 |
def title(self): |
|
849 |
return 'Edit %s source package recipe' % self.context.name |
|
850 |
label = title |
|
851 |
||
11929.11.17
by Tim Penhey
Rework the new page to allow the creation of a ppa on the fly. |
852 |
schema = ISourcePackageEditSchema |
12547.1.2
by Ian Booth
Add inline distro series editing - first cut |
853 |
custom_widget('distroseries', LabeledMultiCheckBoxWidget) |
7675.618.41
by Paul Hummer
Got a working edit |
854 |
|
11814.1.2
by Paul Hummer
Fixed teh bug |
855 |
def setUpFields(self): |
856 |
super(SourcePackageRecipeEditView, self).setUpFields() |
|
857 |
||
12547.1.36
by Ian Booth
Test fixes |
858 |
# Ensure distro series widget allows input
|
859 |
self.form_fields['distroseries'].for_input = True |
|
860 |
||
11814.1.2
by Paul Hummer
Fixed teh bug |
861 |
if check_permission('launchpad.Admin', self.context): |
862 |
# Exclude the PPA archive dropdown.
|
|
863 |
self.form_fields = self.form_fields.omit('daily_build_archive') |
|
864 |
||
865 |
owner_field = self.schema['owner'] |
|
13378.1.1
by j.c.sackett
Undid the rollback by wgrant to get the functionality we need from wallyworld's branch. |
866 |
any_owner_choice = PersonChoice( |
11814.1.2
by Paul Hummer
Fixed teh bug |
867 |
__name__='owner', title=owner_field.title, |
868 |
description=(u"As an administrator you are able to reassign" |
|
869 |
u" this branch to any person or team."), |
|
870 |
required=True, vocabulary='ValidPersonOrTeam') |
|
871 |
any_owner_field = form.Fields( |
|
872 |
any_owner_choice, render_context=self.render_context) |
|
873 |
# Replace the normal owner field with a more permissive vocab.
|
|
874 |
self.form_fields = self.form_fields.omit('owner') |
|
875 |
self.form_fields = any_owner_field + self.form_fields |
|
876 |
||
7675.618.41
by Paul Hummer
Got a working edit |
877 |
@property
|
878 |
def initial_values(self): |
|
879 |
return { |
|
12547.1.2
by Ian Booth
Add inline distro series editing - first cut |
880 |
'distroseries': self.context.distroseries, |
12178.1.1
by Tim Penhey
Use the un-validated recipe text for viewing, and initialising the edit view. |
881 |
'recipe_text': self.context.recipe_text, |
10409.5.105
by Curtis Hovey
Merged devel, resolved conflicts. |
882 |
}
|
7675.618.41
by Paul Hummer
Got a working edit |
883 |
|
884 |
@property
|
|
885 |
def cancel_url(self): |
|
886 |
return canonical_url(self.context) |
|
887 |
||
7675.622.1
by Paul Hummer
Fixing MOST of the concerns in Tim's review (still need to make LaunchpadEditForm work |
888 |
@action('Update Recipe', name='update') |
7675.618.41
by Paul Hummer
Got a working edit |
889 |
def request_action(self, action, data): |
7675.622.3
by Paul Hummer
Added update notification code. |
890 |
changed = False |
891 |
recipe_before_modification = Snapshot( |
|
892 |
self.context, providing=providedBy(self.context)) |
|
893 |
||
894 |
recipe_text = data.pop('recipe_text') |
|
895 |
parser = RecipeParser(recipe_text) |
|
7675.622.4
by Paul Hummer
Fixed a bollocks'd up parser.parse |
896 |
recipe = parser.parse() |
897 |
if self.context.builder_recipe != recipe: |
|
12335.5.2
by Steve Kowalik
Use a new exception to show that an error was raised, and update the two |
898 |
try: |
12335.5.5
by Steve Kowalik
super() isn't needed, switch to self. |
899 |
self.error_handler(self.context.setRecipeText, recipe_text) |
12335.5.3
by Steve Kowalik
And one bit of code cleanup I missed. |
900 |
changed = True |
12335.5.2
by Steve Kowalik
Use a new exception to show that an error was raised, and update the two |
901 |
except ErrorHandled: |
12335.5.1
by Steve Kowalik
* Refactor the error handling for creating and updating a recipe into |
902 |
return
|
10979.1.5
by Paul Hummer
Fixed the failing edit test |
903 |
|
12547.1.2
by Ian Booth
Add inline distro series editing - first cut |
904 |
distros = data.pop('distroseries') |
7675.622.3
by Paul Hummer
Added update notification code. |
905 |
if distros != self.context.distroseries: |
906 |
self.context.distroseries.clear() |
|
907 |
for distroseries_item in distros: |
|
908 |
self.context.distroseries.add(distroseries_item) |
|
909 |
changed = True |
|
910 |
||
911 |
if self.updateContextFromData(data, notify_modified=False): |
|
912 |
changed = True |
|
913 |
||
914 |
if changed: |
|
915 |
field_names = [ |
|
916 |
form_field.__name__ for form_field in self.form_fields] |
|
917 |
notify(ObjectModifiedEvent( |
|
918 |
self.context, recipe_before_modification, field_names)) |
|
7675.618.41
by Paul Hummer
Got a working edit |
919 |
|
920 |
self.next_url = canonical_url(self.context) |
|
7675.618.47
by Paul Hummer
Added delete view |
921 |
|
7675.622.2
by Paul Hummer
Fixed the recipe edit view. |
922 |
@property
|
923 |
def adapters(self): |
|
924 |
"""See `LaunchpadEditFormView`"""
|
|
11929.11.17
by Tim Penhey
Rework the new page to allow the creation of a ppa on the fly. |
925 |
return {ISourcePackageEditSchema: self.context} |
7675.618.49
by Paul Hummer
Merge from recipe-create-update |
926 |
|
7675.711.9
by Paul Hummer
Fixed the test (and the bug) |
927 |
def validate(self, data): |
928 |
super(SourcePackageRecipeEditView, self).validate(data) |
|
929 |
name = data.get('name', None) |
|
7675.711.12
by Paul Hummer
Fixed a potential bug |
930 |
owner = data.get('owner', None) |
931 |
if name and owner: |
|
7675.711.9
by Paul Hummer
Fixed the test (and the bug) |
932 |
SourcePackageRecipeSource = getUtility(ISourcePackageRecipeSource) |
7675.711.12
by Paul Hummer
Fixed a potential bug |
933 |
if SourcePackageRecipeSource.exists(owner, name): |
934 |
recipe = owner.getRecipe(name) |
|
935 |
if recipe != self.context: |
|
7675.711.9
by Paul Hummer
Fixed the test (and the bug) |
936 |
self.setFieldError( |
937 |
'name', |
|
938 |
'There is already a recipe owned by %s with this ' |
|
7675.711.12
by Paul Hummer
Fixed a potential bug |
939 |
'name.' % owner.displayname) |
7675.711.9
by Paul Hummer
Fixed the test (and the bug) |
940 |
|
7675.618.47
by Paul Hummer
Added delete view |
941 |
|
942 |
class SourcePackageRecipeDeleteView(LaunchpadFormView): |
|
943 |
||
944 |
@property
|
|
945 |
def title(self): |
|
946 |
return 'Delete %s source package recipe' % self.context.name |
|
947 |
label = title |
|
948 |
||
949 |
class schema(Interface): |
|
950 |
"""Schema for deleting a branch."""
|
|
951 |
||
952 |
@property
|
|
953 |
def cancel_url(self): |
|
954 |
return canonical_url(self.context) |
|
955 |
||
956 |
@property
|
|
957 |
def next_url(self): |
|
958 |
return canonical_url(self.context.owner) |
|
959 |
||
960 |
@action('Delete recipe', name='delete') |
|
961 |
def request_action(self, action, data): |
|
962 |
self.context.destroySelf() |