sub validate {
# Validates fields containing flag modifications.
+ my $user = Bugzilla->user;
my ($data, $bug_id) = @_;
# Get a list of flags to validate. Uses the "map" function
}
}
}
+
+ # Make sure the user is authorized to modify flags, see bug 180879
+ # - The flag is unchanged
+ next if ($status eq $flag->{status});
+
+ # - User can clear flags set by itself
+ next if (($status eq "X") && ($user->id eq $flag->{setter}));
+
+ # - User in the $grant_gid group can set/clear flags,
+ # including "+" and "-"
+ next if (!$flag->{type}->{grant_gid}
+ || $user->in_group(&::GroupIdToName($flag->{type}->{grant_gid})));
+
+ # - Any other flag modification is denied
+ ThrowUserError("flag_update_denied",
+ { name => $flag->{type}->{name},
+ status => $status,
+ old_status => $flag->{status} });
}
}
("1", "flagtypes.id", "flagtypes.name", "flagtypes.description",
"flagtypes.cc_list", "flagtypes.target_type", "flagtypes.sortkey",
"flagtypes.is_active", "flagtypes.is_requestable",
- "flagtypes.is_requesteeble", "flagtypes.is_multiplicable");
+ "flagtypes.is_requesteeble", "flagtypes.is_multiplicable",
+ "flagtypes.grant_group_id", "flagtypes.request_group_id");
# Note: when adding tables to @base_tables, make sure to include the separator
# (i.e. a comma or words like "LEFT OUTER JOIN") before the table name,
}
sub validate {
+ my $user = Bugzilla->user;
my ($data, $bug_id, $attach_id) = @_;
# Get a list of flag types to validate. Uses the "map" function
attach_id => $attach_id });
}
}
+
+ # Make sure the user is authorized to modify flags, see bug 180879
+ # - User in the $grant_gid group can set flags, including "+" and "-"
+ next if (!$flag_type->{grant_gid}
+ || $user->in_group(&::GroupIdToName($flag_type->{grant_gid})));
+
+ # - User in the $request_gid group can request flags
+ next if ($status eq '?'
+ && (!$flag_type->{request_gid}
+ || $user->in_group(&::GroupIdToName($flag_type->{request_gid}))));
+
+ # - Any other flag modification is denied
+ ThrowUserError("flag_update_denied",
+ { name => $flag_type->{name},
+ status => $status,
+ old_status => "X" });
}
}
push(@$columns, "COUNT(flagexclusions.type_id) AS num_exclusions");
$$having = "num_exclusions = 0";
}
+ if ($criteria->{group}) {
+ my $gid = $criteria->{group};
+ detaint_natural($gid);
+ push(@criteria, "(flagtypes.grant_group_id = $gid " .
+ " OR flagtypes.request_group_id = $gid)");
+ }
return @criteria;
}
$type->{'is_requestable'} = $_[8];
$type->{'is_requesteeble'} = $_[9];
$type->{'is_multiplicable'} = $_[10];
- $type->{'flag_count'} = $_[11];
+ $type->{'grant_gid'} = $_[11];
+ $type->{'request_gid'} = $_[12];
+ $type->{'flag_count'} = $_[13];
return $type;
}
is_requesteeble TINYINT NOT NULL DEFAULT 0 ,
is_multiplicable TINYINT NOT NULL DEFAULT 0 ,
- sortkey SMALLINT NOT NULL DEFAULT 0
+ sortkey SMALLINT NOT NULL DEFAULT 0 ,
+ grant_group_id MEDIUMINT NULL ,
+ request_group_id MEDIUMINT NULL
';
$table{flaginclusions} =
# login data source
AddField("profiles", "extern_id", "varchar(64)");
+# 2004-11-20 - LpSolit@netscape.net - Bug 180879
+# Add grant and request groups for flags
+AddField('flagtypes', 'grant_group_id', 'mediumint null');
+AddField('flagtypes', 'request_group_id', 'mediumint null');
+
# If you had to change the --TABLE-- definition in any way, then add your
# differential change code *** A B O V E *** this comment.
#
sub list {
# Define the variables and functions that will be passed to the UI template.
- $vars->{'bug_types'} = Bugzilla::FlagType::match({ 'target_type' => 'bug' }, 1);
+ $vars->{'bug_types'} =
+ Bugzilla::FlagType::match({ 'target_type' => 'bug',
+ 'group' => $::FORM{'group'} }, 1);
$vars->{'attachment_types'} =
- Bugzilla::FlagType::match({ 'target_type' => 'attachment' }, 1);
+ Bugzilla::FlagType::match({ 'target_type' => 'attachment',
+ 'group' => $::FORM{'group'} }, 1);
# Return the appropriate HTTP response headers.
print Bugzilla->cgi->header();
$vars->{'type'} = Bugzilla::FlagType::get($::FORM{'id'});
$vars->{'type'}->{'inclusions'} = Bugzilla::FlagType::get_inclusions($::FORM{'id'});
$vars->{'type'}->{'exclusions'} = Bugzilla::FlagType::get_exclusions($::FORM{'id'});
+ # Users want to see group names, not IDs
+ foreach my $group ("grant_gid", "request_gid") {
+ my $gid = $vars->{'type'}->{$group};
+ next if (!$gid);
+ SendSQL("SELECT name FROM groups WHERE id = $gid");
+ $vars->{'type'}->{$group} = FetchOneColumn();
+ }
}
# Otherwise set the target type (the minimal information about the type
# that the template needs to know) from the URL parameter and default
validateIsRequestable();
validateIsRequesteeble();
validateAllowMultiple();
+ validateGroups();
my $name = SqlQuote($::FORM{'name'});
my $description = SqlQuote($::FORM{'description'});
# Insert a record for the new flag type into the database.
SendSQL("INSERT INTO flagtypes (id, name, description, cc_list,
target_type, sortkey, is_active, is_requestable,
- is_requesteeble, is_multiplicable)
+ is_requesteeble, is_multiplicable,
+ grant_group_id, request_group_id)
VALUES ($id, $name, $description, $cc_list, '$target_type',
$::FORM{'sortkey'}, $::FORM{'is_active'},
$::FORM{'is_requestable'}, $::FORM{'is_requesteeble'},
- $::FORM{'is_multiplicable'})");
+ $::FORM{'is_multiplicable'}, $::FORM{'grant_gid'},
+ $::FORM{'request_gid'})");
# Populate the list of inclusions/exclusions for this flag type.
foreach my $category_type ("inclusions", "exclusions") {
validateIsRequestable();
validateIsRequesteeble();
validateAllowMultiple();
+ validateGroups();
my $name = SqlQuote($::FORM{'name'});
my $description = SqlQuote($::FORM{'description'});
is_active = $::FORM{'is_active'} ,
is_requestable = $::FORM{'is_requestable'} ,
is_requesteeble = $::FORM{'is_requesteeble'} ,
- is_multiplicable = $::FORM{'is_multiplicable'}
+ is_multiplicable = $::FORM{'is_multiplicable'} ,
+ grant_group_id = $::FORM{'grant_gid'} ,
+ request_group_id = $::FORM{'request_gid'}
WHERE id = $::FORM{'id'}");
# Update the list of inclusions/exclusions for this flag type.
$::FORM{'is_multiplicable'} = $::FORM{'is_multiplicable'} ? 1 : 0;
}
+sub validateGroups {
+ # Convert group names to group IDs
+ foreach my $col ("grant_gid", "request_gid") {
+ my $name = $::FORM{$col};
+ $::FORM{$col} ||= "NULL";
+ next if (!$name);
+ SendSQL("SELECT id FROM groups WHERE name = " . SqlQuote($name));
+ $::FORM{$col} = FetchOneColumn();
+ if (!$::FORM{$col}) {
+ ThrowUserError("group_unknown", { name => $name });
+ }
+ }
+}
$hasproduct = 1;
}
+ my $hasflags = 0;
+ SendSQL("SELECT id FROM flagtypes
+ WHERE grant_group_id = $gid OR request_group_id = $gid");
+ if (FetchOneColumn()) {
+ $hasflags = 1;
+ }
+
$vars->{'gid'} = $gid;
$vars->{'name'} = $name;
$vars->{'description'} = $desc;
$vars->{'hasusers'} = $hasusers;
$vars->{'hasbugs'} = $hasbugs;
$vars->{'hasproduct'} = $hasproduct;
+ $vars->{'hasflags'} = $hasflags;
$vars->{'buglist'} = $buglist;
print Bugzilla->cgi->header();
$cantdelete = 1;
}
}
+ SendSQL("SELECT id FROM flagtypes
+ WHERE grant_group_id = $gid OR request_group_id = $gid");
+ if (FetchOneColumn()) {
+ if (!defined $cgi->param('removeflags')) {
+ $cantdelete = 1;
+ }
+ }
if (!$cantdelete) {
+ SendSQL("UPDATE flagtypes SET grant_group_id = NULL
+ WHERE grant_group_id = $gid");
+ SendSQL("UPDATE flagtypes SET request_group_id = NULL
+ WHERE request_group_id = $gid");
SendSQL("DELETE FROM user_group_map WHERE group_id = $gid");
SendSQL("DELETE FROM group_group_map WHERE grantor_id = $gid");
SendSQL("DELETE FROM bug_group_map WHERE group_id = $gid");
</td>
</tr>
+ <tr>
+ <th>Grant Group:</th>
+ <td>
+ the group allowed to grant/deny flags of this type
+ (to allow all users to grant/deny these flags, leave this empty)<br>
+ <input type="text" name="grant_gid" value="[% type.grant_gid FILTER html %]" size="50" maxlength="255">
+ </td>
+ </tr>
+
+ <tr>
+ <th>Request Group:</th>
+ <td>
+ if flags of this type are requestable, the group allowed to request them
+ (to allow all users to request these flags, leave this empty)<br>
+ Note that the request group alone has no effect if the grant group is not defined!<br>
+ <input type="text" name="request_gid" value="[% type.request_gid FILTER html %]" size="50" maxlength="255">
+ </td>
+ </tr>
+
<tr>
<th></th>
<td>
# hasusers: boolean int. True if the group includes users in it.
# hasbugs: boolean int. True if the group includes bugs in it.
# hasproduct: boolean int. True if the group is binded to a product.
+ # hasflags: boolean int. True if the group is used by a flag type.
# buglist: string. The list of bugs included in this group.
#%]
<br><input type="checkbox" name="unbind">Delete this group anyway,
and make the <U>[% name FILTER html %]</U> publicly visible.</p>
[% END %]
+
+ [% IF hasflags %]
+ <p><b>This group restricts who can make changes to flags of certain types.
+ You cannot delete this group while there are flag types using it.</b>
+
+ <br><a href="editflagtypes.cgi?action=list&group=[% gid FILTER html %]">Show
+ me which types</a> - <input type="checkbox" name="removeflags">Remove all
+ flag types from this group for me.</p>
+ [% END %]
<h2>Confirmation</h2>
<p>Do you really want to delete this group?</p>
- [% IF (hasusers || hasbugs || hasproduct) %]
+ [% IF (hasusers || hasbugs || hasproduct || hasflags) %]
<p><b>You must check all of the above boxes or correct the
indicated problems first before you can proceed.</b></p>
[% END %]
[% title = "Flag Type Name Invalid" %]
The name <em>[% name FILTER html %]</em> must be 1-50 characters long.
+ [% ELSIF error == "flag_update_denied" %]
+ [% title = "Flag Modification Denied" %]
+ You tried to [% IF status == "+" %] grant [% ELSIF status == "-" %] deny
+ [% ELSIF status == "X" %] clear [% ELSE %] request [% END %]
+ <code>[% name FILTER html %]</code>
+ [% IF status == "?" && old_status != "X" %], but this flag is already
+ set[% END %].
+ Only a sufficiently empowered user [% IF status == "X" %] or the user who
+ set <code>[% name FILTER html %][% old_status FILTER html %]</code> in
+ the first place [% END %] can make this change.
+
[% ELSIF error == "format_not_found" %]
[% title = "Format Not Found" %]
The requested format <em>[% format FILTER html %]</em> does not exist with
[% title = "Group not specified" %]
No group was specified.
+ [% ELSIF error == "group_unknown" %]
+ [% title = "Unknown Group" %]
+ The group [% name FILTER html %] does not exist. Please specify
+ a valid group name. Create it first if necessary!
+
[% ELSIF error == "illegal_at_least_x_votes" %]
[% title = "Your Search Makes No Sense" %]
The <em>At least ___ votes</em> field must be a simple number.