summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorByron Jones <glob@mozilla.com>2015-04-21 08:00:18 +0200
committerByron Jones <glob@mozilla.com>2015-04-21 08:00:18 +0200
commit5db6eeb9cf4bf82d785dd193703b46b2139247e5 (patch)
tree2cd234261c4cc56e4035b035b0fc20990ec9a6f6
parent4aa44c46edf93a9707fc693769f39e5aeda7f59b (diff)
downloadbugzilla-5db6eeb9cf4bf82d785dd193703b46b2139247e5.tar.gz
bugzilla-5db6eeb9cf4bf82d785dd193703b46b2139247e5.tar.xz
Bug 579089: Change default Hardware / OS values to be "Unspecified/Unspecified"
-rw-r--r--Bugzilla/Bug.pm13
-rw-r--r--Bugzilla/UserAgent.pm4
-rw-r--r--Bugzilla/WebService/Product.pm3
-rwxr-xr-xenter_bug.cgi10
-rw-r--r--extensions/BMO/Extension.pm120
-rw-r--r--extensions/BMO/template/en/default/hook/admin/params/editparams-current_panel.html.tmpl16
-rw-r--r--extensions/BMO/template/en/default/hook/admin/products/edit-common-rows.html.tmpl35
-rw-r--r--extensions/BMO/template/en/default/hook/admin/products/updated-changes.html.tmpl14
-rw-r--r--extensions/BMO/template/en/default/hook/bug/edit-after_op_sys.html.tmpl45
-rw-r--r--extensions/BugModal/template/en/default/bug_modal/edit.html.tmpl8
-rw-r--r--extensions/GuidedBugEntry/template/en/default/guided/guided.html.tmpl2
-rw-r--r--extensions/GuidedBugEntry/web/js/guided.js15
-rw-r--r--template/en/default/bug/create/create.html.tmpl53
-rw-r--r--template/en/default/bug/edit.html.tmpl2
14 files changed, 283 insertions, 57 deletions
diff --git a/Bugzilla/Bug.pm b/Bugzilla/Bug.pm
index e1f533274..6dbcffe34 100644
--- a/Bugzilla/Bug.pm
+++ b/Bugzilla/Bug.pm
@@ -702,10 +702,15 @@ sub create {
unless defined $params->{bug_severity};
$params->{priority} = Bugzilla->params->{defaultpriority}
unless defined $params->{priority};
- $params->{op_sys} = Bugzilla->params->{defaultopsys}
- unless defined $params->{op_sys};
- $params->{rep_platform} = Bugzilla->params->{defaultplatform}
- unless defined $params->{rep_platform};
+
+ # BMO - per-product hw/os defaults
+ if (!defined $params->{rep_platform} || !defined $params->{op_sys}) {
+ if (my $product = Bugzilla::Product->new({ name => $params->{product}, cache => 1 })) {
+ $params->{rep_platform} //= $product->default_product;
+ $params->{op_sys} //= $product->default_op_sys;
+ }
+ }
+
# Make sure a comment is always defined.
$params->{comment} = '' unless defined $params->{comment};
diff --git a/Bugzilla/UserAgent.pm b/Bugzilla/UserAgent.pm
index 62d6115a9..9c8686f0c 100644
--- a/Bugzilla/UserAgent.pm
+++ b/Bugzilla/UserAgent.pm
@@ -183,7 +183,7 @@ use constant OS_MAP => (
);
sub detect_platform {
- my $userAgent = $ENV{'HTTP_USER_AGENT'} || '';
+ my $userAgent = shift || Bugzilla->cgi->user_agent || '';
my @detected;
my $iterator = natatime(2, PLATFORMS_MAP);
while (my($re, $ra) = $iterator->()) {
@@ -195,7 +195,7 @@ sub detect_platform {
}
sub detect_op_sys {
- my $userAgent = $ENV{'HTTP_USER_AGENT'} || '';
+ my $userAgent = shift || Bugzilla->cgi->user_agent || '';
my @detected;
my $iterator = natatime(2, OS_MAP);
while (my($re, $ra) = $iterator->()) {
diff --git a/Bugzilla/WebService/Product.pm b/Bugzilla/WebService/Product.pm
index be082c778..b7484327f 100644
--- a/Bugzilla/WebService/Product.pm
+++ b/Bugzilla/WebService/Product.pm
@@ -202,6 +202,9 @@ sub _product_to_hash {
$self->_milestone_to_hash($_, $params)
} @{$product->milestones}];
}
+ # BMO - add default hw/os
+ $field_data->{default_platform} = $self->type('string', $product->default_platform);
+ $field_data->{default_op_sys} = $self->type('string', $product->default_op_sys);
return filter($params, $field_data);
}
diff --git a/enter_bug.cgi b/enter_bug.cgi
index 171b4566f..56f1061c8 100755
--- a/enter_bug.cgi
+++ b/enter_bug.cgi
@@ -313,10 +313,12 @@ else {
$default{'component_'} = formvalue('component');
$default{'priority'} = formvalue('priority', Bugzilla->params->{'defaultpriority'});
$default{'bug_severity'} = formvalue('bug_severity', Bugzilla->params->{'defaultseverity'});
- $default{'rep_platform'} = formvalue('rep_platform',
- Bugzilla->params->{'defaultplatform'} || detect_platform());
- $default{'op_sys'} = formvalue('op_sys',
- Bugzilla->params->{'defaultopsys'} || detect_op_sys());
+
+ # BMO - use per-product default hw/os
+ $default{'rep_platform'} = formvalue('rep_platform', $product->default_platform // detect_platform());
+ $default{'op_sys'} = formvalue('op_sys', $product->default_op_sys // detect_op_sys());
+ $vars->{'rep_platform'} = detect_platform();
+ $vars->{'rep_op_sys'} = detect_op_sys();
$vars->{'alias'} = formvalue('alias');
$vars->{'short_desc'} = formvalue('short_desc');
diff --git a/extensions/BMO/Extension.pm b/extensions/BMO/Extension.pm
index 06c07cc83..717f347de 100644
--- a/extensions/BMO/Extension.pm
+++ b/extensions/BMO/Extension.pm
@@ -31,12 +31,13 @@ use Bugzilla::Error;
use Bugzilla::Field;
use Bugzilla::Field::Choice;
use Bugzilla::Group;
+use Bugzilla::Install::Filesystem;
use Bugzilla::Mailer;
use Bugzilla::Product;
use Bugzilla::Status;
use Bugzilla::Token;
-use Bugzilla::Install::Filesystem;
use Bugzilla::User;
+use Bugzilla::UserAgent qw(detect_platform detect_op_sys);
use Bugzilla::User::Setting;
use Bugzilla::Util;
@@ -62,12 +63,17 @@ our $VERSION = '0.1';
#
BEGIN {
- *Bugzilla::Bug::last_closed_date = \&_last_closed_date;
- *Bugzilla::Product::default_security_group = \&_default_security_group;
- *Bugzilla::Product::default_security_group_obj = \&_default_security_group_obj;
- *Bugzilla::Product::group_always_settable = \&_group_always_settable;
+ *Bugzilla::Bug::last_closed_date = \&_last_closed_date;
+ *Bugzilla::Bug::reporters_hw_os = \&_bug_reporters_hw_os;
+ *Bugzilla::Product::default_security_group = \&_default_security_group;
+ *Bugzilla::Product::default_security_group_obj = \&_default_security_group_obj;
+ *Bugzilla::Product::group_always_settable = \&_group_always_settable;
+ *Bugzilla::Product::default_platform_id = \&_product_default_platform_id;
+ *Bugzilla::Product::default_op_sys_id = \&_product_default_op_sys_id;
+ *Bugzilla::Product::default_platform = \&_product_default_platform;
+ *Bugzilla::Product::default_op_sys = \&_product_default_op_sys;
*Bugzilla::check_default_product_security_group = \&_check_default_product_security_group;
- *Bugzilla::Attachment::is_bounty_attachment = \&_is_bounty_attachment;
+ *Bugzilla::Attachment::is_bounty_attachment = \&_is_bounty_attachment;
}
sub template_before_process {
@@ -641,6 +647,43 @@ sub quicksearch_map {
}
}
+sub object_columns {
+ my ($self, $args) = @_;
+ return unless $args->{class}->isa('Bugzilla::Product');
+ push @{ $args->{columns} }, qw( default_platform_id default_op_sys_id );
+}
+
+sub object_update_columns {
+ my ($self, $args) = @_;
+ return unless $args->{object}->isa('Bugzilla::Product');
+ push @{ $args->{columns} }, qw( default_platform_id default_op_sys_id );
+}
+
+sub object_before_create {
+ my ($self, $args) = @_;
+ return unless $args->{class}->isa('Bugzilla::Product');
+
+ my $cgi = Bugzilla->cgi;
+ my $params = $args->{params};
+ foreach my $field (qw( default_platform_id default_op_sys_id )) {
+ $params->{$field} = $cgi->param($field);
+ }
+}
+
+sub object_end_of_set_all {
+ my ($self, $args) = @_;
+ my $object = $args->{object};
+ return unless $object->isa('Bugzilla::Product');
+
+ my $cgi = Bugzilla->cgi;
+ my $params = $args->{params};
+ foreach my $field (qw( default_platform_id default_op_sys_id )) {
+ my $value = $cgi->param($field);
+ detaint_natural($value);
+ $object->set($field, $value);
+ }
+}
+
sub object_end_of_create {
my ($self, $args) = @_;
my $class = $args->{class};
@@ -675,6 +718,52 @@ sub object_end_of_create {
}
}
+sub _bug_reporters_hw_os {
+ my ($self) = @_;
+ return $self->{ua_hw_os} if exists $self->{ua_hw_os};
+ my $memcached = Bugzilla->memcached;
+ my $hw_os = $memcached->get({ key => 'bug.ua.' . $self->id });
+ if (!$hw_os) {
+ (my $ua) = Bugzilla->dbh->selectrow_array(
+ "SELECT user_agent FROM bug_user_agent WHERE bug_id = ?",
+ undef,
+ $self->id);
+ $hw_os = $ua
+ ? [ detect_platform($ua), detect_op_sys($ua) ]
+ : [];
+ $memcached->set({ key => 'bug.ua.' . $self->id, value => $hw_os });
+ }
+ return $self->{ua_hw_os} = $hw_os;
+}
+
+sub _product_default_platform_id { $_[0]->{default_platform_id} }
+sub _product_default_op_sys_id { $_[0]->{default_op_sys_id} }
+
+sub _product_default_platform {
+ my ($self) = @_;
+ if (!exists $self->{default_platform}) {
+ $self->{default_platform} = $self->default_platform_id
+ ? Bugzilla::Field::Choice
+ ->type('rep_platform')
+ ->new($_[0]->{default_platform_id})
+ ->name
+ : undef;
+ }
+ return $self->{default_platform};
+}
+sub _product_default_op_sys {
+ my ($self) = @_;
+ if (!exists $self->{default_op_sys}) {
+ $self->{default_op_sys} = $self->default_op_sys_id
+ ? Bugzilla::Field::Choice
+ ->type('op_sys')
+ ->new($_[0]->{default_op_sys_id})
+ ->name
+ : undef;
+ }
+ return $self->{default_op_sys};
+}
+
sub _get_named_query {
my ($sharer_id, $group_id, $definition) = @_;
my $dbh = Bugzilla->dbh;
@@ -701,11 +790,11 @@ sub _get_named_query {
return $namedquery_id;
}
-# Automatically CC users to bugs based on group & product
sub bug_end_of_create {
my ($self, $args) = @_;
my $bug = $args->{'bug'};
+ # automatically CC users to bugs based on group & product
foreach my $group_name (keys %group_auto_cc) {
my $group_obj = Bugzilla::Group->new({ name => $group_name });
if ($group_obj && $bug->in_group($group_obj)) {
@@ -717,6 +806,21 @@ sub bug_end_of_create {
}
}
}
+
+ # store user-agent
+ if (my $ua = Bugzilla->cgi->user_agent) {
+ trick_taint($ua);
+ Bugzilla->dbh->do(
+ "INSERT INTO bug_user_agent (bug_id, user_agent) VALUES (?, ?)",
+ undef,
+ $bug->id, $ua
+ );
+ }
+}
+
+sub db_sanitize {
+ print "deleting reporter's user-agents...\n";
+ Bugzilla->dbh->do("TRUNCATE TABLE bug_user_agent");
}
# bugs in an ASSIGNED state must be assigned to a real person
@@ -872,7 +976,7 @@ sub db_schema_abstract_schema {
REFERENCES => {
TABLE => 'bugs',
COLUMN => 'bug_id',
- DELETE => 'cascade',
+ DELETE => 'CASCADE',
},
},
user_agent => {
diff --git a/extensions/BMO/template/en/default/hook/admin/params/editparams-current_panel.html.tmpl b/extensions/BMO/template/en/default/hook/admin/params/editparams-current_panel.html.tmpl
index 39f063464..9b251431c 100644
--- a/extensions/BMO/template/en/default/hook/admin/params/editparams-current_panel.html.tmpl
+++ b/extensions/BMO/template/en/default/hook/admin/params/editparams-current_panel.html.tmpl
@@ -6,8 +6,14 @@
# defined by the Mozilla Public License, v. 2.0.
#%]
-[% IF panel.name == "groupsecurity" %]
- [% panel.param_descs.delete_comments_group =
- 'The name of the group of users who can delete comments by using the "deleted" comment tag.'
- %]
-[% END -%]
+[%
+IF panel.name == "groupsecurity";
+ panel.param_descs.delete_comments_group =
+ 'The name of the group of users who can delete comments by using the "deleted" comment tag.';
+
+ELSIF panel.name == "bugfields";
+ panel.param_descs.defaultplatform = "This parameter is ignored on BMO, use per-product defaults instead.";
+ panel.param_descs.defaultopsys = "This parameter is ignored on BMO, use per-product defaults instead.";
+
+END;
+%]
diff --git a/extensions/BMO/template/en/default/hook/admin/products/edit-common-rows.html.tmpl b/extensions/BMO/template/en/default/hook/admin/products/edit-common-rows.html.tmpl
new file mode 100644
index 000000000..7093bcfc6
--- /dev/null
+++ b/extensions/BMO/template/en/default/hook/admin/products/edit-common-rows.html.tmpl
@@ -0,0 +1,35 @@
+[%# This Source Code Form is subject to the terms of the Mozilla Public
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ #
+ # This Source Code Form is "Incompatible With Secondary Licenses", as
+ # defined by the Mozilla Public License, v. 2.0.
+ #%]
+
+<tr>
+ <th align="right">Default Platform</th>
+ <td>
+ [% INCLUDE default_select
+ field_name = 'default_platform_id'
+ field_value = product.default_platform_id
+ field_values = bug_fields.rep_platform.legal_values
+ %]
+ [%= INCLUDE default_select
+ field_name = 'default_op_sys_id'
+ field_value = product.default_op_sys_id
+ field_values = bug_fields.op_sys.legal_values
+ %]
+ </td>
+</tr>
+
+[% BLOCK default_select %]
+ <select name="[% field_name FILTER html %]">
+ <option value="" [% " selected" IF field_value == "" %]>Detect</option>
+ [% FOREACH v IN field_values %]
+ [% NEXT UNLESS v.is_active %]
+ <option value="[% v.id FILTER html %]" [% " selected" IF field_value == v.id %]>
+ [% v.value FILTER html %]
+ </option>
+ [% END %]
+ </select>
+[% END %]
diff --git a/extensions/BMO/template/en/default/hook/admin/products/updated-changes.html.tmpl b/extensions/BMO/template/en/default/hook/admin/products/updated-changes.html.tmpl
new file mode 100644
index 000000000..af480134e
--- /dev/null
+++ b/extensions/BMO/template/en/default/hook/admin/products/updated-changes.html.tmpl
@@ -0,0 +1,14 @@
+[%# This Source Code Form is subject to the terms of the Mozilla Public
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ #
+ # This Source Code Form is "Incompatible With Secondary Licenses", as
+ # defined by the Mozilla Public License, v. 2.0.
+ #%]
+
+[% IF changes.default_platform_id.defined %]
+ <p>Default Platform updated</p>
+[% END %]
+[% IF changes.default_op_sys_id.defined %]
+ <p>Default Op-Sys updated</p>
+[% END %]
diff --git a/extensions/BMO/template/en/default/hook/bug/edit-after_op_sys.html.tmpl b/extensions/BMO/template/en/default/hook/bug/edit-after_op_sys.html.tmpl
new file mode 100644
index 000000000..b5c3a722b
--- /dev/null
+++ b/extensions/BMO/template/en/default/hook/bug/edit-after_op_sys.html.tmpl
@@ -0,0 +1,45 @@
+[%# This Source Code Form is subject to the terms of the Mozilla Public
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ #
+ # This Source Code Form is "Incompatible With Secondary Licenses", as
+ # defined by the Mozilla Public License, v. 2.0.
+ #%]
+
+[%# because the "from reporter" button adds likely unnecessary noise to a bug,
+ # we only show it on unconfirmed or untriaged bugs %]
+[%
+ RETURN UNLESS
+ (bug.status.value == "UNCONFIRMED" || bug.component == "Untriaged")
+ && bug.check_can_change_field('op_sys', 0, 1);
+ hw_os = bug.reporters_hw_os;
+ RETURN UNLESS hw_os.size;
+ RETURN IF bug.rep_platform == hw_os.0 && bug.op_sys == hw_os.1;
+
+ title = "Set platform to reporter's: " _ hw_os.0 _ " / " _ hw_os.1;
+%]
+
+[% onclick = BLOCK %]
+ $('#rep_platform').val('[% hw_os.0 FILTER js FILTER html %]');
+ $('#op_sys').val('[% hw_os.1 FILTER js FILTER html %]');
+ $('#rep_hw_os').hide();
+[% END %]
+[%
+ IF bug_modal;
+ INCLUDE modal;
+ ELSE;
+ INCLUDE classic;
+ END;
+%]
+
+[% BLOCK classic %]
+ <span id="rep_hw_os">
+ (<a href="javascript:void(0)" title="[% title FILTER html %]" onclick="[% onclick FILTER none %]">from reporter</a>)
+ </span>
+[% END %]
+
+[% BLOCK modal %]
+ <button id="rep_hw_os" type="button" class="minor" title="[% title FILTER html %]" onclick="[% onclick FILTER none %]">
+ From&nbsp;Reporter
+ </button>
+[% END %]
diff --git a/extensions/BugModal/template/en/default/bug_modal/edit.html.tmpl b/extensions/BugModal/template/en/default/bug_modal/edit.html.tmpl
index 75f4747ea..3f6330423 100644
--- a/extensions/BugModal/template/en/default/bug_modal/edit.html.tmpl
+++ b/extensions/BugModal/template/en/default/bug_modal/edit.html.tmpl
@@ -9,6 +9,8 @@
[%
USE Bugzilla;
+ bug_modal = 1;
+
# only edit one bug
UNLESS bug.defined;
bug = bugs.0;
@@ -620,6 +622,12 @@
field_type = constants.FIELD_TYPE_SINGLE_SELECT
inline = 1
%]
+ [% WRAPPER bug_modal/field.html.tmpl
+ container = 1
+ inline = 1
+ %]
+ [% Hook.process("after_op_sys", 'bug/edit.html.tmpl') %]
+ [% END %]
[% END %]
[%# keywords %]
diff --git a/extensions/GuidedBugEntry/template/en/default/guided/guided.html.tmpl b/extensions/GuidedBugEntry/template/en/default/guided/guided.html.tmpl
index c3119810b..47614a636 100644
--- a/extensions/GuidedBugEntry/template/en/default/guided/guided.html.tmpl
+++ b/extensions/GuidedBugEntry/template/en/default/guided/guided.html.tmpl
@@ -55,8 +55,6 @@ YAHOO.util.Dom.removeClass('loading', 'hidden');
<script type="text/javascript">
YAHOO.util.Dom.addClass('loading', 'hidden');
guided.init({ webdev: [% webdev ? "true" : "false" %] });
-guided.detectedPlatform = '[% platform FILTER js %]';
-guided.detectedOpSys = '[% op_sys FILTER js %]';
guided.currentUser = '[% user.login FILTER js %]';
guided.openStates = [
[% FOREACH state = open_states %]
diff --git a/extensions/GuidedBugEntry/web/js/guided.js b/extensions/GuidedBugEntry/web/js/guided.js
index d7fe2c75b..0ff438782 100644
--- a/extensions/GuidedBugEntry/web/js/guided.js
+++ b/extensions/GuidedBugEntry/web/js/guided.js
@@ -14,8 +14,6 @@ var History = YAHOO.util.History;
var guided = {
_currentStep: '',
_defaultStep: 'product',
- detectedPlatform: '',
- detectedOpSys: '',
currentUser: '',
openStates: [],
updateStep: true,
@@ -198,15 +196,6 @@ var product = {
Dom.get('groups').value = products['_default'].secgroup;
}
- // use the correct platform & op_sys
- if (products[productName] && products[productName].detectPlatform) {
- Dom.get('rep_platform').value = guided.detectedPlatform;
- Dom.get('op_sys').value = guided.detectedOpSys;
- } else {
- Dom.get('rep_platform').value = 'All';
- Dom.get('op_sys').value = 'All';
- }
-
// show support message
if (products[productName] && products[productName].support) {
Dom.get("product_support_message").innerHTML = products[productName].support;
@@ -834,6 +823,10 @@ var bugForm = {
elVersions.value = '';
}
bugForm.onVersionChange(elVersions.value);
+
+ // set default hw/os
+ Dom.get('rep_platform').value = product.details.default_platform;
+ Dom.get('op_sys').value = product.details.default_op_sys;
},
onComponentChange: function(componentName) {
diff --git a/template/en/default/bug/create/create.html.tmpl b/template/en/default/bug/create/create.html.tmpl
index e258a0d73..fc1bc5420 100644
--- a/template/en/default/bug/create/create.html.tmpl
+++ b/template/en/default/bug/create/create.html.tmpl
@@ -321,31 +321,42 @@ TUI_hide_default('attachment_text_field');
value = default.bug_severity %]
</tr>
+ [% needs_extra_tr = 1 %]
<tr>
- [% INCLUDE bug/field.html.tmpl
- bug = default, field = bug_fields.rep_platform, editable = 1,
- value = default.rep_platform %]
+ <th>
+ Platform:
+ </th>
+ <td>
+ <div>
+ <span title="Hardware">
+ [% INCLUDE bug/field.html.tmpl
+ no_tds = 1, bug = default, field = bug_fields.rep_platform, editable = 1,
+ value = default.rep_platform %]
+ </span>
+ <span title="Operating System">
+ [% INCLUDE bug/field.html.tmpl
+ no_tds = 1, bug = default, field = bug_fields.op_sys, editable = 1,
+ value = default.op_sys %]
+ </span>
+ </div>
+ [% IF !cloned_bug_id %]
+ <div class="comment">
+ Update the <i>platform</i> field if this [% terms.bug %] is applicable to specific platforms.<br>
+ <span id="rep_hw_os">
+ (<a href="javascript:void(0)"
+ onclick="$('#rep_platform').val('[% rep_platform FILTER js FILTER html %]');
+ $('#op_sys').val('[% rep_op_sys FILTER js FILTER html %]');
+ $('#rep_hw_os').css('visibility', 'hidden')">use my platform</a>)
+ </span>
+ </div>
+ [% needs_extra_tr = 0 %]
+ [% END %]
+ </td>
</tr>
- <tr>
- [% INCLUDE bug/field.html.tmpl
- bug = default, field = bug_fields.op_sys, editable = 1,
- value = default.op_sys %]
- </tr>
- [% IF (!Param('defaultplatform') || !Param('defaultopsys')) && !cloned_bug_id %]
+ [% IF needs_extra_tr %]
<tr>
- <th colspan="3">&nbsp;</th>
- <td id="os_guess_note" class="comment">
- <div>We've made a guess at your
- [% IF Param('defaultplatform') %]
- operating system. Please check it
- [% ELSIF Param('defaultopsys') %]
- platform. Please check it
- [% ELSE %]
- operating system and platform. Please check them
- [% END %]
- and make any corrections if necessary.</div>
- </td>
+ <td>&nbsp;</td>
</tr>
[% END %]
</tbody>
diff --git a/template/en/default/bug/edit.html.tmpl b/template/en/default/bug/edit.html.tmpl
index c9d81b34c..0fc8762a4 100644
--- a/template/en/default/bug/edit.html.tmpl
+++ b/template/en/default/bug/edit.html.tmpl
@@ -368,6 +368,8 @@
bug = bug, field = bug_fields.op_sys,
no_tds = 1, value = bug.op_sys
editable = bug.check_can_change_field('op_sys', 0, 1) %]
+ [%# BMO - hook for hw/os detection from reporter %]
+ [% Hook.process('after_op_sys', 'bug/edit.html.tmpl') %]
</td>
</tr>