1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
|
= The Project Scope Widget =
Many application front pages contain a search form. The search
can usually be across the whole Launchpad or only in one project.
The ProjectScopeWidget is used to select that scope. The scope type
is actually determined by the field's vocabulary. In this example,
we will use the Project vocabulary which allows any project to be
selected.
>>> from zope.schema import Choice
>>> from canonical.launchpad.webapp.servers import LaunchpadTestRequest
>>> from canonical.launchpad.webapp.testing import verifyObject
>>> from lp.app.widgets.project import ProjectScopeWidget
>>> empty_request = LaunchpadTestRequest()
>>> scope_field = Choice(
... __name__='scope', vocabulary='ProjectGroup', required=False)
>>> scope_field = scope_field.bind(object())
>>> widget = ProjectScopeWidget(
... scope_field, scope_field.vocabulary, empty_request)
The widget complies to both IInputWidget and IBrowserWidget.
>>> from zope.app.form.browser.interfaces import IBrowserWidget
>>> from zope.app.form.interfaces import IInputWidget
>>> verifyObject(IInputWidget, widget)
True
>>> verifyObject(IBrowserWidget, widget)
True
When the request is empty, the widget doesn't have any input:
>>> widget.hasInput()
False
>>> widget.hasValidInput()
False
It's a radio widget, so it assumes that it always will have input. If
the widget isn't required, getInputValue() returns None if there
isn't any input.
>>> widget.required
False
>>> widget.getInputValue() is None
True
If the widget is required, getInputValue() raises UnexpectedFormData if
there is no input.
>>> widget.required = True
>>> widget.getInputValue()
Traceback (most recent call last):
...
UnexpectedFormData: No valid option was selected.
By default, the 'All projects' scope is selected:
>>> print widget()
<label>
<input class="radioType" checked="checked"
id="field.scope.option.all" name="field.scope"
type="radio" value="all" />
All projects
</label>
<label>
<input class="radioType" id="field.scope.option.project"
name="field.scope"
onclick="document.getElementById('field.scope.target').focus();"
type="radio" value="project"
/>
One project:
...
== Selecting All Projects ==
When the 'All projects' option is selected, the widget returns None.
>>> form = {'field.scope': 'all',
... 'field.scope.target': ''}
>>> widget = ProjectScopeWidget(
... scope_field, scope_field.vocabulary,
... LaunchpadTestRequest(form=form))
>>> widget.hasInput()
True
>>> widget.getInputValue() is None
True
== Selecting One Project ==
If we select a project, the project with that name is returned
by getInputValue().
>>> form = {'field.scope': 'project',
... 'field.scope.target': 'mozilla'}
>>> from lp.registry.interfaces.projectgroup import IProjectGroup
>>> widget = ProjectScopeWidget(
... scope_field, scope_field.vocabulary,
... LaunchpadTestRequest(form=form))
>>> widget.hasInput()
True
>>> selected_scope = widget.getInputValue()
>>> IProjectGroup.providedBy(selected_scope)
True
>>> selected_scope.name
u'mozilla'
If an non-existant distribution name is provided, a widget error is
raised:
>>> form['field.scope.target'] = 'invalid'
>>> widget = ProjectScopeWidget(
... scope_field, scope_field.vocabulary,
... LaunchpadTestRequest(form=form))
>>> widget.hasInput()
True
>>> selected_scope = widget.getInputValue()
Traceback (most recent call last):
...
LaunchpadValidationError: There is no project named 'invalid'
registered in Launchpad
The same error text is returned by error():
>>> print widget.error()
There is no project named 'invalid' registered in Launchpad
If no project name is given at all, a widget error is also raised:
>>> form['field.scope.target'] = ''
>>> widget = ProjectScopeWidget(
... scope_field, scope_field.vocabulary,
... LaunchpadTestRequest(form=form))
>>> widget.hasInput()
True
>>> selected_scope = widget.getInputValue()
Traceback (most recent call last):
...
LaunchpadValidationError: Please enter a project name
>>> print widget.error()
Please enter a project name
>>> del form['field.scope.target']
>>> widget = ProjectScopeWidget(
... scope_field, scope_field.vocabulary,
... LaunchpadTestRequest(form=form))
>>> widget.hasInput()
True
>>> selected_scope = widget.getInputValue()
Traceback (most recent call last):
...
LaunchpadValidationError: Please enter a project name
>>> print widget.error()
Please enter a project name
== setRenderedValue() ==
In order to initialize the widget with a value, setRenderedValue() is
used. Passing a product to it will select the 'One project' radio
button, as well as displaying the product name in the project widget.
>>> from lp.registry.interfaces.projectgroup import IProjectGroupSet
>>> widget = ProjectScopeWidget(
... scope_field, scope_field.vocabulary, empty_request)
>>> projectgroups = getUtility(IProjectGroupSet)
>>> widget.setRenderedValue(projectgroups.getByName('mozilla'))
>>> print widget()
<label>
<input class="radioType" id="field.scope.option.all"
name="field.scope" type="radio" value="all" />
...
<input class="radioType" checked="checked"
id="field.scope.option.project" name="field.scope"
onclick="document.getElementById('field.scope.target').focus();"
type="radio" value="project" />
...
<input type="text" value="mozilla" id="field.scope.target"
name="field.scope.target" size="20"
maxlength=""
onKeyPress="selectWidget('field.scope.option.project', event)" style=""
class="" />
...
Setting the scope to None, will default to the 'All projects' option.
>>> widget.setRenderedValue(None)
>>> print widget()
<label>
<input class="radioType" checked="checked"
id="field.scope.option.all" name="field.scope"
type="radio" value="all" />
...
== getScope() and partial queries ==
In some cases, forms with a ProjectScopeWidget are requested by bots
which incorrectly build the query string without without the `scope`
parameter. A method, `getScope` is provided, which returns the value
of the scope option, or `None` if no scope was selected.
>>> form = {'field.scope': 'project',
... 'field.scope.target': 'mozilla'}
>>> widget = ProjectScopeWidget(
... scope_field, scope_field.vocabulary,
... LaunchpadTestRequest(form=form))
>>> print widget.getScope()
project
>>> form = {'field.scope': 'all',
... 'field.scope.target': ''}
>>> widget = ProjectScopeWidget(
... scope_field, scope_field.vocabulary,
... LaunchpadTestRequest(form=form))
>>> print widget.getScope()
all
>>> form = {'field.scope.target': ''}
>>> widget = ProjectScopeWidget(
... scope_field, scope_field.vocabulary,
... LaunchpadTestRequest(form=form))
>>> print widget.getScope()
None
|