enter_bug.cgi 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602
  1. #!/usr/bin/env perl -wT
  2. # -*- Mode: perl; indent-tabs-mode: nil -*-
  3. #
  4. # The contents of this file are subject to the Mozilla Public
  5. # License Version 1.1 (the "License"); you may not use this file
  6. # except in compliance with the License. You may obtain a copy of
  7. # the License at http://www.mozilla.org/MPL/
  8. #
  9. # Software distributed under the License is distributed on an "AS
  10. # IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  11. # implied. See the License for the specific language governing
  12. # rights and limitations under the License.
  13. #
  14. # The Original Code is the Bugzilla Bug Tracking System.
  15. #
  16. # The Initial Developer of the Original Code is Netscape Communications
  17. # Corporation. Portions created by Netscape are Copyright (C) 1998
  18. # Netscape Communications Corporation. All Rights Reserved.
  19. #
  20. # Contributor(s): Terry Weissman <terry@mozilla.org>
  21. # Dave Miller <justdave@syndicomm.com>
  22. # Joe Robins <jmrobins@tgix.com>
  23. # Gervase Markham <gerv@gerv.net>
  24. # Shane H. W. Travis <travis@sedsystems.ca>
  25. ##############################################################################
  26. #
  27. # enter_bug.cgi
  28. # -------------
  29. # Displays bug entry form. Bug fields are specified through popup menus,
  30. # drop-down lists, or text fields. Default for these values can be
  31. # passed in as parameters to the cgi.
  32. #
  33. ##############################################################################
  34. use strict;
  35. use lib qw(. lib);
  36. use Bugzilla;
  37. use Bugzilla::Constants;
  38. use Bugzilla::Util;
  39. use Bugzilla::Error;
  40. use Bugzilla::Bug;
  41. use Bugzilla::User;
  42. use Bugzilla::Hook;
  43. use Bugzilla::Product;
  44. use Bugzilla::Classification;
  45. use Bugzilla::Keyword;
  46. use Bugzilla::Token;
  47. use Bugzilla::Field;
  48. use Bugzilla::Status;
  49. my $user = Bugzilla->login(LOGIN_REQUIRED);
  50. my $cloned_bug;
  51. my $cloned_bug_id;
  52. my $cgi = Bugzilla->cgi;
  53. my $dbh = Bugzilla->dbh;
  54. my $template = Bugzilla->template;
  55. my $vars = {};
  56. # All pages point to the same part of the documentation.
  57. $vars->{'doc_section'} = 'bugreports.html';
  58. my $product_name = trim($cgi->param('product') || '');
  59. # Will contain the product object the bug is created in.
  60. my $product;
  61. if ($product_name eq '') {
  62. # If the user cannot enter bugs in any product, stop here.
  63. my @enterable_products = @{$user->get_enterable_products};
  64. ThrowUserError('no_products') unless scalar(@enterable_products);
  65. my $classification = Bugzilla->params->{'useclassification'} ?
  66. scalar($cgi->param('classification')) : '__all';
  67. # Unless a real classification name is given, we sort products
  68. # by classification.
  69. my @classifications;
  70. unless ($classification && $classification ne '__all') {
  71. if (Bugzilla->params->{'useclassification'}) {
  72. my $class;
  73. # Get all classifications with at least one enterable product.
  74. foreach my $product (@enterable_products) {
  75. $class->{$product->classification_id}->{'object'} ||=
  76. new Bugzilla::Classification($product->classification_id);
  77. # Nice way to group products per classification, without querying
  78. # the DB again.
  79. push(@{$class->{$product->classification_id}->{'products'}}, $product);
  80. }
  81. @classifications = sort {$a->{'object'}->sortkey <=> $b->{'object'}->sortkey
  82. || lc($a->{'object'}->name) cmp lc($b->{'object'}->name)}
  83. (values %$class);
  84. }
  85. else {
  86. @classifications = ({object => undef, products => \@enterable_products});
  87. }
  88. }
  89. unless ($classification) {
  90. # We know there is at least one classification available,
  91. # else we would have stopped earlier.
  92. if (scalar(@classifications) > 1) {
  93. # We only need classification objects.
  94. $vars->{'classifications'} = [map {$_->{'object'}} @classifications];
  95. $vars->{'target'} = "enter_bug.cgi";
  96. $vars->{'format'} = $cgi->param('format');
  97. $vars->{'cloned_bug_id'} = $cgi->param('cloned_bug_id');
  98. print $cgi->header();
  99. $template->process("global/choose-classification.html.tmpl", $vars)
  100. || ThrowTemplateError($template->error());
  101. exit;
  102. }
  103. # If we come here, then there is only one classification available.
  104. $classification = $classifications[0]->{'object'}->name;
  105. }
  106. # Keep only enterable products which are in the specified classification.
  107. if ($classification ne "__all") {
  108. my $class = new Bugzilla::Classification({'name' => $classification});
  109. # If the classification doesn't exist, then there is no product in it.
  110. if ($class) {
  111. @enterable_products
  112. = grep {$_->classification_id == $class->id} @enterable_products;
  113. @classifications = ({object => $class, products => \@enterable_products});
  114. }
  115. else {
  116. @enterable_products = ();
  117. }
  118. }
  119. if (scalar(@enterable_products) == 0) {
  120. ThrowUserError('no_products');
  121. }
  122. elsif (scalar(@enterable_products) > 1) {
  123. $vars->{'classifications'} = \@classifications;
  124. $vars->{'target'} = "enter_bug.cgi";
  125. $vars->{'format'} = $cgi->param('format');
  126. $vars->{'cloned_bug_id'} = $cgi->param('cloned_bug_id');
  127. print $cgi->header();
  128. $template->process("global/choose-product.html.tmpl", $vars)
  129. || ThrowTemplateError($template->error());
  130. exit;
  131. } else {
  132. # Only one product exists.
  133. $product = $enterable_products[0];
  134. }
  135. }
  136. else {
  137. # Do not use Bugzilla::Product::check_product() here, else the user
  138. # could know whether the product doesn't exist or is not accessible.
  139. $product = new Bugzilla::Product({'name' => $product_name});
  140. }
  141. # We need to check and make sure that the user has permission
  142. # to enter a bug against this product.
  143. $user->can_enter_product($product ? $product->name : $product_name, THROW_ERROR);
  144. ##############################################################################
  145. # Useful Subroutines
  146. ##############################################################################
  147. sub formvalue {
  148. my ($name, $default) = (@_);
  149. return Bugzilla->cgi->param($name) || $default || "";
  150. }
  151. # Takes the name of a field and a list of possible values for that
  152. # field. Returns the first value in the list that is actually a
  153. # valid value for that field.
  154. # The field should be named after its DB table.
  155. # Returns undef if none of the platforms match.
  156. sub pick_valid_field_value (@) {
  157. my ($field, @values) = @_;
  158. my $dbh = Bugzilla->dbh;
  159. foreach my $value (@values) {
  160. return $value if $dbh->selectrow_array(
  161. "SELECT 1 FROM $field WHERE value = ?", undef, $value);
  162. }
  163. return undef;
  164. }
  165. sub pickplatform {
  166. return formvalue("rep_platform") if formvalue("rep_platform");
  167. my @platform;
  168. if (Bugzilla->params->{'defaultplatform'}) {
  169. @platform = Bugzilla->params->{'defaultplatform'};
  170. } else {
  171. # If @platform is a list, this function will return the first
  172. # item in the list that is a valid platform choice. If
  173. # no choice is valid, we return "Other".
  174. for ($ENV{'HTTP_USER_AGENT'}) {
  175. #PowerPC
  176. /\(.*PowerPC.*\)/i && do {@platform = "Macintosh"; last;};
  177. /\(.*PPC.*\)/ && do {@platform = "Macintosh"; last;};
  178. /\(.*AIX.*\)/ && do {@platform = "Macintosh"; last;};
  179. #Intel x86
  180. /\(.*Intel.*\)/ && do {@platform = "PC"; last;};
  181. /\(.*[ix0-9]86.*\)/ && do {@platform = "PC"; last;};
  182. #Versions of Windows that only run on Intel x86
  183. /\(.*Win(?:dows |)[39M].*\)/ && do {@platform = "PC"; last};
  184. /\(.*Win(?:dows |)16.*\)/ && do {@platform = "PC"; last;};
  185. #Sparc
  186. /\(.*sparc.*\)/ && do {@platform = "Sun"; last;};
  187. /\(.*sun4.*\)/ && do {@platform = "Sun"; last;};
  188. #Alpha
  189. /\(.*AXP.*\)/i && do {@platform = "DEC"; last;};
  190. /\(.*[ _]Alpha.\D/i && do {@platform = "DEC"; last;};
  191. /\(.*[ _]Alpha\)/i && do {@platform = "DEC"; last;};
  192. #MIPS
  193. /\(.*IRIX.*\)/i && do {@platform = "SGI"; last;};
  194. /\(.*MIPS.*\)/i && do {@platform = "SGI"; last;};
  195. #68k
  196. /\(.*68K.*\)/ && do {@platform = "Macintosh"; last;};
  197. /\(.*680[x0]0.*\)/ && do {@platform = "Macintosh"; last;};
  198. #HP
  199. /\(.*9000.*\)/ && do {@platform = "HP"; last;};
  200. #ARM
  201. # /\(.*ARM.*\) && do {$platform = "ARM";};
  202. #Stereotypical and broken
  203. /\(.*Macintosh.*\)/ && do {@platform = "Macintosh"; last;};
  204. /\(.*Mac OS [89].*\)/ && do {@platform = "Macintosh"; last;};
  205. /\(Win.*\)/ && do {@platform = "PC"; last;};
  206. /\(.*Win(?:dows[ -])NT.*\)/ && do {@platform = "PC"; last;};
  207. /\(.*OSF.*\)/ && do {@platform = "DEC"; last;};
  208. /\(.*HP-?UX.*\)/i && do {@platform = "HP"; last;};
  209. /\(.*IRIX.*\)/i && do {@platform = "SGI"; last;};
  210. /\(.*(SunOS|Solaris).*\)/ && do {@platform = "Sun"; last;};
  211. #Braindead old browsers who didn't follow convention:
  212. /Amiga/ && do {@platform = "Macintosh"; last;};
  213. /WinMosaic/ && do {@platform = "PC"; last;};
  214. }
  215. }
  216. return pick_valid_field_value('rep_platform', @platform) || "Other";
  217. }
  218. sub pickos {
  219. if (formvalue('op_sys') ne "") {
  220. return formvalue('op_sys');
  221. }
  222. my @os = ();
  223. if (Bugzilla->params->{'defaultopsys'}) {
  224. @os = Bugzilla->params->{'defaultopsys'};
  225. } else {
  226. # This function will return the first
  227. # item in @os that is a valid platform choice. If
  228. # no choice is valid, we return "Other".
  229. for ($ENV{'HTTP_USER_AGENT'}) {
  230. /\(.*IRIX.*\)/ && do {push @os, "IRIX"; };
  231. /\(.*OSF.*\)/ && do {push @os, "OSF/1";};
  232. /\(.*Linux.*\)/ && do {push @os, "Linux";};
  233. /\(.*Solaris.*\)/ && do {push @os, "Solaris";};
  234. /\(.*SunOS.*\)/ && do {
  235. /\(.*SunOS 5.11.*\)/ && do {push @os, ("OpenSolaris", "Opensolaris", "Solaris 11");};
  236. /\(.*SunOS 5.10.*\)/ && do {push @os, "Solaris 10";};
  237. /\(.*SunOS 5.9.*\)/ && do {push @os, "Solaris 9";};
  238. /\(.*SunOS 5.8.*\)/ && do {push @os, "Solaris 8";};
  239. /\(.*SunOS 5.7.*\)/ && do {push @os, "Solaris 7";};
  240. /\(.*SunOS 5.6.*\)/ && do {push @os, "Solaris 6";};
  241. /\(.*SunOS 5.5.*\)/ && do {push @os, "Solaris 5";};
  242. /\(.*SunOS 5.*\)/ && do {push @os, "Solaris";};
  243. /\(.*SunOS.*sun4u.*\)/ && do {push @os, "Solaris";};
  244. /\(.*SunOS.*i86pc.*\)/ && do {push @os, "Solaris";};
  245. /\(.*SunOS.*\)/ && do {push @os, "SunOS";};
  246. };
  247. /\(.*HP-?UX.*\)/ && do {push @os, "HP-UX";};
  248. /\(.*BSD.*\)/ && do {
  249. /\(.*BSD\/(?:OS|386).*\)/ && do {push @os, "BSDI";};
  250. /\(.*FreeBSD.*\)/ && do {push @os, "FreeBSD";};
  251. /\(.*OpenBSD.*\)/ && do {push @os, "OpenBSD";};
  252. /\(.*NetBSD.*\)/ && do {push @os, "NetBSD";};
  253. };
  254. /\(.*BeOS.*\)/ && do {push @os, "BeOS";};
  255. /\(.*AIX.*\)/ && do {push @os, "AIX";};
  256. /\(.*OS\/2.*\)/ && do {push @os, "OS/2";};
  257. /\(.*QNX.*\)/ && do {push @os, "Neutrino";};
  258. /\(.*VMS.*\)/ && do {push @os, "OpenVMS";};
  259. /\(.*Win.*\)/ && do {
  260. /\(.*Windows XP.*\)/ && do {push @os, "Windows XP";};
  261. /\(.*Windows NT 6\.0.*\)/ && do {push @os, "Windows Vista";};
  262. /\(.*Windows NT 5\.2.*\)/ && do {push @os, "Windows Server 2003";};
  263. /\(.*Windows NT 5\.1.*\)/ && do {push @os, "Windows XP";};
  264. /\(.*Windows 2000.*\)/ && do {push @os, "Windows 2000";};
  265. /\(.*Windows NT 5.*\)/ && do {push @os, "Windows 2000";};
  266. /\(.*Win.*9[8x].*4\.9.*\)/ && do {push @os, "Windows ME";};
  267. /\(.*Win(?:dows |)M[Ee].*\)/ && do {push @os, "Windows ME";};
  268. /\(.*Win(?:dows |)98.*\)/ && do {push @os, "Windows 98";};
  269. /\(.*Win(?:dows |)95.*\)/ && do {push @os, "Windows 95";};
  270. /\(.*Win(?:dows |)16.*\)/ && do {push @os, "Windows 3.1";};
  271. /\(.*Win(?:dows[ -]|)NT.*\)/ && do {push @os, "Windows NT";};
  272. /\(.*Windows.*NT.*\)/ && do {push @os, "Windows NT";};
  273. };
  274. /\(.*Mac OS X.*\)/ && do {
  275. /\(.*Intel.*Mac OS X 10.5.*\)/ && do {push @os, "Mac OS X 10.5";};
  276. /\(.*Intel.*Mac OS X.*\)/ && do {push @os, "Mac OS X 10.4";};
  277. /\(.*Mac OS X.*\)/ && do {push @os, ("Mac OS X 10.3", "Mac OS X 10.0", "Mac OS X");};
  278. };
  279. /\(.*32bit.*\)/ && do {push @os, "Windows 95";};
  280. /\(.*16bit.*\)/ && do {push @os, "Windows 3.1";};
  281. /\(.*Mac OS \d.*\)/ && do {
  282. /\(.*Mac OS 9.*\)/ && do {push @os, ("Mac System 9.x", "Mac System 9.0");};
  283. /\(.*Mac OS 8\.6.*\)/ && do {push @os, ("Mac System 8.6", "Mac System 8.5");};
  284. /\(.*Mac OS 8\.5.*\)/ && do {push @os, "Mac System 8.5";};
  285. /\(.*Mac OS 8\.1.*\)/ && do {push @os, ("Mac System 8.1", "Mac System 8.0");};
  286. /\(.*Mac OS 8\.0.*\)/ && do {push @os, "Mac System 8.0";};
  287. /\(.*Mac OS 8[^.].*\)/ && do {push @os, "Mac System 8.0";};
  288. /\(.*Mac OS 8.*\)/ && do {push @os, "Mac System 8.6";};
  289. };
  290. /\(.*Darwin.*\)/ && do {push @os, ("Mac OS X 10.0", "Mac OS X");};
  291. # Silly
  292. /\(.*Mac.*\)/ && do {
  293. /\(.*Mac.*PowerPC.*\)/ && do {push @os, "Mac System 9.x";};
  294. /\(.*Mac.*PPC.*\)/ && do {push @os, "Mac System 9.x";};
  295. /\(.*Mac.*68k.*\)/ && do {push @os, "Mac System 8.0";};
  296. };
  297. # Evil
  298. /Amiga/i && do {push @os, "Other";};
  299. /WinMosaic/ && do {push @os, "Windows 95";};
  300. /\(.*PowerPC.*\)/ && do {push @os, "Mac System 9.x";};
  301. /\(.*PPC.*\)/ && do {push @os, "Mac System 9.x";};
  302. /\(.*68K.*\)/ && do {push @os, "Mac System 8.0";};
  303. }
  304. }
  305. push(@os, "Windows") if grep(/^Windows /, @os);
  306. push(@os, "Mac OS") if grep(/^Mac /, @os);
  307. return pick_valid_field_value('op_sys', @os) || "Other";
  308. }
  309. ##############################################################################
  310. # End of subroutines
  311. ##############################################################################
  312. my $has_editbugs = $user->in_group('editbugs', $product->id);
  313. my $has_canconfirm = $user->in_group('canconfirm', $product->id);
  314. # If a user is trying to clone a bug
  315. # Check that the user has authorization to view the parent bug
  316. # Create an instance of Bug that holds the info from the parent
  317. $cloned_bug_id = $cgi->param('cloned_bug_id');
  318. if ($cloned_bug_id) {
  319. ValidateBugID($cloned_bug_id);
  320. $cloned_bug = new Bugzilla::Bug($cloned_bug_id);
  321. }
  322. if (scalar(@{$product->components}) == 1) {
  323. # Only one component; just pick it.
  324. $cgi->param('component', $product->components->[0]->name);
  325. }
  326. my %default;
  327. $vars->{'product'} = $product;
  328. $vars->{'priority'} = get_legal_field_values('priority');
  329. $vars->{'bug_severity'} = get_legal_field_values('bug_severity');
  330. $vars->{'rep_platform'} = get_legal_field_values('rep_platform');
  331. $vars->{'op_sys'} = get_legal_field_values('op_sys');
  332. $vars->{'use_keywords'} = 1 if Bugzilla::Keyword::keyword_count();
  333. $vars->{'assigned_to'} = formvalue('assigned_to');
  334. $vars->{'assigned_to_disabled'} = !$has_editbugs;
  335. $vars->{'cc_disabled'} = 0;
  336. $vars->{'qa_contact'} = formvalue('qa_contact');
  337. $vars->{'qa_contact_disabled'} = !$has_editbugs;
  338. $vars->{'cloned_bug_id'} = $cloned_bug_id;
  339. $vars->{'token'} = issue_session_token('createbug:');
  340. my @enter_bug_fields = grep { $_->enter_bug } Bugzilla->active_custom_fields;
  341. foreach my $field (@enter_bug_fields) {
  342. $vars->{$field->name} = formvalue($field->name);
  343. }
  344. if ($cloned_bug_id) {
  345. $default{'component_'} = $cloned_bug->component;
  346. $default{'priority'} = $cloned_bug->priority;
  347. $default{'bug_severity'} = $cloned_bug->bug_severity;
  348. $default{'rep_platform'} = $cloned_bug->rep_platform;
  349. $default{'op_sys'} = $cloned_bug->op_sys;
  350. $vars->{'short_desc'} = $cloned_bug->short_desc;
  351. $vars->{'bug_file_loc'} = $cloned_bug->bug_file_loc;
  352. $vars->{'keywords'} = $cloned_bug->keywords;
  353. $vars->{'dependson'} = $cloned_bug_id;
  354. $vars->{'blocked'} = "";
  355. $vars->{'deadline'} = $cloned_bug->deadline;
  356. if (defined $cloned_bug->cc) {
  357. $vars->{'cc'} = join (", ", @{$cloned_bug->cc});
  358. } else {
  359. $vars->{'cc'} = formvalue('cc');
  360. }
  361. foreach my $field (@enter_bug_fields) {
  362. my $field_name = $field->name;
  363. $vars->{$field_name} = $cloned_bug->$field_name;
  364. }
  365. # We need to ensure that we respect the 'insider' status of
  366. # the first comment, if it has one. Either way, make a note
  367. # that this bug was cloned from another bug.
  368. # We cannot use $cloned_bug->longdescs because this method
  369. # depends on the "comment_sort_order" user pref, and we
  370. # really want the first comment of the bug.
  371. my $bug_desc = Bugzilla::Bug::GetComments($cloned_bug_id, 'oldest_to_newest');
  372. my $isprivate = $bug_desc->[0]->{'isprivate'};
  373. $vars->{'comment'} = "";
  374. $vars->{'commentprivacy'} = 0;
  375. if (!$isprivate || Bugzilla->user->is_insider) {
  376. $vars->{'comment'} = $bug_desc->[0]->{'body'};
  377. $vars->{'commentprivacy'} = $isprivate;
  378. }
  379. } # end of cloned bug entry form
  380. else {
  381. $default{'component_'} = formvalue('component');
  382. $default{'priority'} = formvalue('priority', Bugzilla->params->{'defaultpriority'});
  383. $default{'bug_severity'} = formvalue('bug_severity', Bugzilla->params->{'defaultseverity'});
  384. $default{'rep_platform'} = pickplatform();
  385. $default{'op_sys'} = pickos();
  386. $vars->{'short_desc'} = formvalue('short_desc');
  387. $vars->{'bug_file_loc'} = formvalue('bug_file_loc', "http://");
  388. $vars->{'keywords'} = formvalue('keywords');
  389. $vars->{'dependson'} = formvalue('dependson');
  390. $vars->{'blocked'} = formvalue('blocked');
  391. $vars->{'deadline'} = formvalue('deadline');
  392. $vars->{'cc'} = join(', ', $cgi->param('cc'));
  393. $vars->{'comment'} = formvalue('comment');
  394. $vars->{'commentprivacy'} = formvalue('commentprivacy');
  395. } # end of normal/bookmarked entry form
  396. # IF this is a cloned bug,
  397. # AND the clone's product is the same as the parent's
  398. # THEN use the version from the parent bug
  399. # ELSE IF a version is supplied in the URL
  400. # THEN use it
  401. # ELSE IF there is a version in the cookie
  402. # THEN use it (Posting a bug sets a cookie for the current version.)
  403. # ELSE
  404. # The default version is the last one in the list (which, it is
  405. # hoped, will be the most recent one).
  406. #
  407. # Eventually maybe each product should have a "current version"
  408. # parameter.
  409. $vars->{'version'} = [map($_->name, @{$product->versions})];
  410. if ( ($cloned_bug_id) &&
  411. ($product->name eq $cloned_bug->product ) ) {
  412. $default{'version'} = $cloned_bug->version;
  413. } elsif (formvalue('version')) {
  414. $default{'version'} = formvalue('version');
  415. } elsif (defined $cgi->cookie("VERSION-" . $product->name) &&
  416. lsearch($vars->{'version'}, $cgi->cookie("VERSION-" . $product->name)) != -1) {
  417. $default{'version'} = $cgi->cookie("VERSION-" . $product->name);
  418. } else {
  419. $default{'version'} = $vars->{'version'}->[$#{$vars->{'version'}}];
  420. }
  421. # Get list of milestones.
  422. if ( Bugzilla->params->{'usetargetmilestone'} ) {
  423. $vars->{'target_milestone'} = [map($_->name, @{$product->milestones})];
  424. if (formvalue('target_milestone')) {
  425. $default{'target_milestone'} = formvalue('target_milestone');
  426. } else {
  427. $default{'target_milestone'} = $product->default_milestone;
  428. }
  429. }
  430. # Construct the list of allowable statuses.
  431. my $initial_statuses = Bugzilla::Status->can_change_to();
  432. # Exclude closed states from the UI, even if the workflow allows them.
  433. # The back-end code will still accept them, though.
  434. @$initial_statuses = grep { $_->is_open } @$initial_statuses;
  435. my @status = map { $_->name } @$initial_statuses;
  436. # UNCONFIRMED is illegal if votes_to_confirm = 0.
  437. @status = grep {$_ ne 'UNCONFIRMED'} @status unless $product->votes_to_confirm;
  438. scalar(@status) || ThrowUserError('no_initial_bug_status');
  439. # If the user has no privs...
  440. unless ($has_editbugs || $has_canconfirm) {
  441. # ... use UNCONFIRMED if available, else use the first status of the list.
  442. my $bug_status = (grep {$_ eq 'UNCONFIRMED'} @status) ? 'UNCONFIRMED' : $status[0];
  443. @status = ($bug_status);
  444. }
  445. $vars->{'bug_status'} = \@status;
  446. # Get the default from a template value if it is legitimate.
  447. # Otherwise, and only if the user has privs, set the default
  448. # to the first confirmed bug status on the list, if available.
  449. if (formvalue('bug_status') && (lsearch(\@status, formvalue('bug_status')) >= 0)) {
  450. $default{'bug_status'} = formvalue('bug_status');
  451. } elsif (scalar @status == 1) {
  452. $default{'bug_status'} = $status[0];
  453. }
  454. else {
  455. $default{'bug_status'} = ($status[0] ne 'UNCONFIRMED') ? $status[0] : $status[1];
  456. }
  457. my $grouplist = $dbh->selectall_arrayref(
  458. q{SELECT DISTINCT groups.id, groups.name, groups.description,
  459. membercontrol, othercontrol
  460. FROM groups
  461. LEFT JOIN group_control_map
  462. ON group_id = id AND product_id = ?
  463. WHERE isbuggroup != 0 AND isactive != 0
  464. ORDER BY description}, undef, $product->id);
  465. my @groups;
  466. foreach my $row (@$grouplist) {
  467. my ($id, $groupname, $description, $membercontrol, $othercontrol) = @$row;
  468. # Only include groups if the entering user will have an option.
  469. next if ((!$membercontrol)
  470. || ($membercontrol == CONTROLMAPNA)
  471. || ($membercontrol == CONTROLMAPMANDATORY)
  472. || (($othercontrol != CONTROLMAPSHOWN)
  473. && ($othercontrol != CONTROLMAPDEFAULT)
  474. && (!Bugzilla->user->in_group($groupname)))
  475. );
  476. my $check;
  477. # If this is a cloned bug,
  478. # AND the product for this bug is the same as for the original
  479. # THEN set a group's checkbox if the original also had it on
  480. # ELSE IF this is a bookmarked template
  481. # THEN set a group's checkbox if was set in the bookmark
  482. # ELSE
  483. # set a groups's checkbox based on the group control map
  484. #
  485. if ( ($cloned_bug_id) &&
  486. ($product->name eq $cloned_bug->product ) ) {
  487. foreach my $i (0..(@{$cloned_bug->groups} - 1) ) {
  488. if ($cloned_bug->groups->[$i]->{'bit'} == $id) {
  489. $check = $cloned_bug->groups->[$i]->{'ison'};
  490. }
  491. }
  492. }
  493. elsif(formvalue("maketemplate") ne "") {
  494. $check = formvalue("bit-$id", 0);
  495. }
  496. else {
  497. # Checkbox is checked by default if $control is a default state.
  498. $check = (($membercontrol == CONTROLMAPDEFAULT)
  499. || (($othercontrol == CONTROLMAPDEFAULT)
  500. && (!Bugzilla->user->in_group($groupname))));
  501. }
  502. my $group =
  503. {
  504. 'bit' => $id ,
  505. 'checked' => $check ,
  506. 'description' => $description
  507. };
  508. push @groups, $group;
  509. }
  510. $vars->{'group'} = \@groups;
  511. Bugzilla::Hook::process("enter_bug-entrydefaultvars", { vars => $vars });
  512. $vars->{'default'} = \%default;
  513. my $format = $template->get_format("bug/create/create",
  514. scalar $cgi->param('format'),
  515. scalar $cgi->param('ctype'));
  516. print $cgi->header($format->{'ctype'});
  517. $template->process($format->{'template'}, $vars)
  518. || ThrowTemplateError($template->error());