@@ -15,9 +15,12 @@ use Bugzilla::Bug;
15
15
use Bugzilla::Constants;
16
16
use Bugzilla::Error;
17
17
use Bugzilla::Field;
18
+ use Bugzilla::Group;
18
19
use Bugzilla::Keyword;
19
20
use Bugzilla::Milestone;
21
+ use Bugzilla::Product;
20
22
use Bugzilla::Version;
23
+ use List::MoreUtils qw( any first_value) ;
21
24
22
25
# these methods are much lighter than our public API calls
23
26
@@ -33,6 +36,7 @@ sub rest_resources {
33
36
},
34
37
},
35
38
},
39
+
36
40
# returns pre-formatted html, enabling reuse of the user template
37
41
qr { ^/bug_modal/cc/(\d +)$} , {
38
42
GET => {
@@ -42,6 +46,18 @@ sub rest_resources {
42
46
},
43
47
},
44
48
},
49
+
50
+ # returns fields that require touching when the product is changed
51
+ qw{ ^/bug_modal/new_product/(\d+)$} , {
52
+ GET => {
53
+ method => ' new_product' ,
54
+ params => sub {
55
+ # products with slashes in their name means we have to grab
56
+ # the product from the query-string instead of the path
57
+ return { id => $_ [0], product_name => Bugzilla-> input_params-> {product } }
58
+ },
59
+ },
60
+ },
45
61
]
46
62
}
47
63
@@ -59,15 +75,15 @@ sub edit {
59
75
unless (grep { $_ -> id == $bug -> product_id } @products ) {
60
76
unshift @products , $bug -> product_obj;
61
77
}
62
- $options {product } = [ map { { name => $_ -> name, description => $_ -> description } } @products ];
78
+ $options {product } = [ map { { name => $_ -> name } } @products ];
63
79
64
- $options {component } = _name_desc ($bug -> component , $bug -> product_obj -> components );
65
- $options {version } = _name($bug -> version , $bug -> product_obj -> versions );
66
- $options {target_milestone } = _name($bug -> target_milestone , $bug -> product_obj -> milestones );
67
- $options {priority } = _name($bug -> priority, ' priority ' );
68
- $options {bug_severity } = _name($bug -> bug_severity, ' bug_severity ' );
69
- $options {rep_platform } = _name($bug -> rep_platform, ' rep_platform ' );
70
- $options {op_sys } = _name($bug -> op_sys, ' op_sys ' );
80
+ $options {component } = _name ($bug -> product_obj -> components , $bug -> component );
81
+ $options {version } = _name($bug -> product_obj -> versions , $bug -> version );
82
+ $options {target_milestone } = _name($bug -> product_obj -> milestones , $bug -> target_milestone );
83
+ $options {priority } = _name(' priority ' , $bug -> priority);
84
+ $options {bug_severity } = _name(' bug_severity ' , $bug -> bug_severity);
85
+ $options {rep_platform } = _name(' rep_platform ' , $bug -> rep_platform);
86
+ $options {op_sys } = _name(' op_sys ' , $bug -> op_sys);
71
87
72
88
# custom select fields
73
89
my @custom_fields =
@@ -93,27 +109,15 @@ sub edit {
93
109
}
94
110
95
111
sub _name {
96
- my ($current , $values ) = @_ ;
112
+ my ($values , $current ) = @_ ;
97
113
# values can either be an array-ref of values, or a field name, which
98
114
# result in that field's legal-values being used.
99
115
if (!ref ($values )) {
100
116
$values = Bugzilla::Field-> new({ name => $values , cache => 1 })-> legal_values;
101
117
}
102
118
return [
103
119
map { { name => $_ -> name } }
104
- grep { $_ -> name eq $current || $_ -> is_active }
105
- @$values
106
- ];
107
- }
108
-
109
- sub _name_desc {
110
- my ($current , $values ) = @_ ;
111
- if (!ref ($values )) {
112
- $values = Bugzilla::Field-> new({ name => $values , cache => 1 })-> legal_values;
113
- }
114
- return [
115
- map { { name => $_ -> name, description => $_ -> description } }
116
- grep { $_ -> name eq $current || $_ -> is_active }
120
+ grep { (defined $current && $_ -> name eq $current ) || $_ -> is_active }
117
121
@$values
118
122
];
119
123
}
@@ -135,4 +139,188 @@ sub cc {
135
139
return { html => $html };
136
140
}
137
141
142
+ sub new_product {
143
+ my ($self , $params ) = @_ ;
144
+ my $dbh = Bugzilla-> dbh;
145
+ my $user = Bugzilla-> user;
146
+ my $bug = Bugzilla::Bug-> check({ id => $params -> {id } });
147
+ my $product = Bugzilla::Product-> check({ name => $params -> {product_name }, cache => 1 });
148
+ my $true = $self -> type(' boolean' , 1);
149
+ my %result ;
150
+
151
+ # components
152
+
153
+ my $components = _name($product -> components);
154
+ my $current_component = $bug -> component;
155
+ if (my $component = first_value { $_ -> {name } eq $current_component } @$components ) {
156
+ # identical component in both products
157
+ $component -> {selected } = $true ;
158
+ }
159
+ else {
160
+ # default to a blank value
161
+ unshift @$components , {
162
+ name => ' ' ,
163
+ selected => $true ,
164
+ };
165
+ }
166
+ $result {component } = $components ;
167
+
168
+ # milestones
169
+
170
+ my $milestones = _name($product -> milestones);
171
+ my $current_milestone = $bug -> target_milestone;
172
+ if ($bug -> check_can_change_field(' target_milestone' , 0, 1)
173
+ && (my $milestone = first_value { $_ -> {name } eq $current_milestone } @$milestones ))
174
+ {
175
+ # identical milestone in both products
176
+ $milestone -> {selected } = $true ;
177
+ }
178
+ else {
179
+ # use default milestone
180
+ my $default_milestone = $product -> default_milestone;
181
+ my $milestone = first_value { $_ -> {name } eq $default_milestone } @$milestones ;
182
+ $milestone -> {selected } = $true ;
183
+ }
184
+ $result {target_milestone } = $milestones ;
185
+
186
+ # versions
187
+
188
+ my $versions = _name($product -> versions);
189
+ my $current_version = $bug -> version;
190
+ my $selected_version ;
191
+ if (my $version = first_value { $_ -> {name } eq $current_version } @$versions ) {
192
+ # identical version in both products
193
+ $version -> {selected } = $true ;
194
+ $selected_version = $version ;
195
+ }
196
+ elsif (
197
+ $current_version =~ / ^(\d +) Branch$ /
198
+ || $current_version =~ / ^Firefox (\d +)$ /
199
+ || $current_version =~ / ^(\d +)$ / )
200
+ {
201
+ # firefox, with its three version naming schemes
202
+ my $branch = $1 ;
203
+ foreach my $test_version (" $branch Branch" , " Firefox $branch " , $branch ) {
204
+ if (my $version = first_value { $_ -> {name } eq $test_version } @$versions ) {
205
+ $version -> {selected } = $true ;
206
+ $selected_version = $version ;
207
+ last ;
208
+ }
209
+ }
210
+ }
211
+ if (!$selected_version ) {
212
+ # "unspecified", "other"
213
+ foreach my $test_version (" unspecified" , " other" ) {
214
+ if (my $version = first_value { lc ($_ -> {name }) eq $test_version } @$versions ) {
215
+ $version -> {selected } = $true ;
216
+ $selected_version = $version ;
217
+ last ;
218
+ }
219
+ }
220
+ }
221
+ if (!$selected_version ) {
222
+ # default to a blank value
223
+ unshift @$versions , {
224
+ name => ' ' ,
225
+ selected => $true ,
226
+ };
227
+ }
228
+ $result {version } = $versions ;
229
+
230
+ # groups
231
+
232
+ my @groups ;
233
+
234
+ # find invalid groups
235
+ push @groups ,
236
+ map {{
237
+ type => ' invalid' ,
238
+ group => $_ ,
239
+ checked => 0,
240
+ }}
241
+ @{ Bugzilla::Bug-> get_invalid_groups({ bug_ids => [ $bug -> id ], product => $product }) };
242
+
243
+ # logic lifted from bug/process/verify-new-product.html.tmpl
244
+ my $current_groups = $bug -> groups_in;
245
+ my $group_controls = $product -> group_controls;
246
+ foreach my $group_id (keys %$group_controls ) {
247
+ my $group_control = $group_controls -> {$group_id };
248
+ if ($group_control -> {membercontrol } == CONTROLMAPMANDATORY
249
+ || ($group_control -> {othercontrol } == CONTROLMAPMANDATORY && !$user -> in_group($group_control -> {name })))
250
+ {
251
+ # mandatory, always checked
252
+ push @groups , {
253
+ type => ' mandatory' ,
254
+ group => $group_control -> {group },
255
+ checked => 1,
256
+ };
257
+ }
258
+ elsif (
259
+ ($group_control -> {membercontrol } != CONTROLMAPNA && $user -> in_group($group_control -> {name }))
260
+ || $group_control -> {othercontrol } != CONTROLMAPNA)
261
+ {
262
+ # optional, checked if..
263
+ my $group = $group_control -> {group };
264
+ my $checked =
265
+ # same group as current product
266
+ (any { $_ -> id == $group -> id } @$current_groups )
267
+ # member default
268
+ || $group_control -> {membercontrol } == CONTROLMAPDEFAULT && $user -> in_group($group_control -> {name })
269
+ # or other default
270
+ || $group_control -> {othercontrol } == CONTROLMAPDEFAULT && !$user -> in_group($group_control -> {name })
271
+ ;
272
+ push @groups , {
273
+ type => ' optional' ,
274
+ group => $group_control -> {group },
275
+ checked => $checked || 0,
276
+ };
277
+ }
278
+ }
279
+
280
+ my $default_group_name = $product -> default_security_group;
281
+ if (my $default_group = first_value { $_ -> {group }-> name eq $default_group_name } @groups ) {
282
+ # because we always allow the default product group to be selected, it's never invalid
283
+ $default_group -> {type } = ' optional' if $default_group -> {type } eq ' invalid' ;
284
+ }
285
+ else {
286
+ # add the product's default group if it's missing
287
+ unshift @groups , {
288
+ type => ' optional' ,
289
+ group => $product -> default_security_group_obj,
290
+ checked => 0,
291
+ };
292
+ }
293
+
294
+ # if the bug is currently in a group, ensure a group is checked by default
295
+ # by checking the product's default group if no other groups apply
296
+ if (@$current_groups && !any { $_ -> {checked } } @groups ) {
297
+ foreach my $g (@groups ) {
298
+ next unless $g -> {group }-> name eq $default_group_name ;
299
+ $g -> {checked } = 1;
300
+ last ;
301
+ }
302
+ }
303
+
304
+ # group by type and flatten
305
+ my $vars = {
306
+ product => $product ,
307
+ groups => { invalid => [], mandatory => [], optional => [] },
308
+ };
309
+ foreach my $g (@groups ) {
310
+ push @{ $vars -> {groups }-> {$g -> {type }} }, {
311
+ id => $g -> {group }-> id,
312
+ name => $g -> {group }-> name,
313
+ description => $g -> {group }-> description,
314
+ checked => $g -> {checked },
315
+ };
316
+ }
317
+
318
+ # build group selection html
319
+ my $template = Bugzilla-> template;
320
+ $template -> process(' bug_modal/new_product_groups.html.tmpl' , $vars , \$result {groups })
321
+ || ThrowTemplateError($template -> error);
322
+
323
+ return \%result ;
324
+ }
325
+
138
326
1;
0 commit comments