Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
bugzilla
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
etersoft
bugzilla
Commits
120b63d5
You need to sign in or sign up before continuing.
Commit
120b63d5
authored
Feb 15, 2010
by
Max Kanat-Alexander
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Bug 372979: Make voting into an extension
r=mkanat, a=mkanat, a=LpSolit
parent
7802dbcf
Hide whitespace changes
Inline
Side-by-side
Showing
72 changed files
with
1577 additions
and
1284 deletions
+1577
-1284
.bzrignore
.bzrignore
+0
-1
Bug.pm
Bugzilla/Bug.pm
+1
-157
BugMail.pm
Bugzilla/BugMail.pm
+2
-9
Comment.pm
Bugzilla/Comment.pm
+1
-1
BugFields.pm
Bugzilla/Config/BugFields.pm
+0
-6
Constants.pm
Bugzilla/Constants.pm
+3
-5
Schema.pm
Bugzilla/DB/Schema.pm
+0
-27
Field.pm
Bugzilla/Field.pm
+0
-1
DB.pm
Bugzilla/Install/DB.pm
+30
-24
Object.pm
Bugzilla/Object.pm
+2
-1
Product.pm
Bugzilla/Product.pm
+0
-147
Search.pm
Bugzilla/Search.pm
+2
-16
Quicksearch.pm
Bugzilla/Search/Quicksearch.pm
+0
-18
Bug.pm
Bugzilla/WebService/Bug.pm
+2
-8
buglist.cgi
buglist.cgi
+0
-18
bugzilla.dtd
bugzilla.dtd
+1
-2
colchange.cgi
colchange.cgi
+0
-3
merge-users.pl
contrib/merge-users.pl
+0
-1
bzLifecycle.xml
docs/en/images/bzLifecycle.xml
+1
-2
editproducts.cgi
editproducts.cgi
+7
-15
editusers.cgi
editusers.cgi
+0
-4
Extension.pm
extensions/Voting/Extension.pm
+861
-0
email-relationships.html.tmpl
.../default/hook/account/prefs/email-relationships.html.tmpl
+22
-0
edit-common-rows.html.tmpl
...en/default/hook/admin/products/edit-common-rows.html.tmpl
+60
-0
updated-changes.html.tmpl
.../en/default/hook/admin/products/updated-changes.html.tmpl
+102
-0
messages-statuses.html.tmpl
...efault/hook/admin/sanitycheck/messages-statuses.html.tmpl
+40
-0
confirm-delete-warn_safe.html.tmpl
...fault/hook/admin/users/confirm-delete-warn_safe.html.tmpl
+38
-0
edit-after_importance.html.tmpl
...plate/en/default/hook/bug/edit-after_importance.html.tmpl
+41
-0
format_comment-type.txt.tmpl
...template/en/default/hook/bug/format_comment-type.txt.tmpl
+23
-0
header-title.html.tmpl
...mplate/en/default/hook/bug/process/header-title.html.tmpl
+24
-0
results-title.html.tmpl
...plate/en/default/hook/bug/process/results-title.html.tmpl
+21
-0
field-descs-end.none.tmpl
...template/en/default/hook/global/field-descs-end.none.tmpl
+22
-0
reason-descs-end.none.tmpl
...emplate/en/default/hook/global/reason-descs-end.none.tmpl
+23
-0
user-error-errors.html.tmpl
...mplate/en/default/hook/global/user-error-errors.html.tmpl
+55
-0
form-email_numbering_end.html.tmpl
...en/default/hook/search/form-email_numbering_end.html.tmpl
+31
-0
search-report-select-rep_fields.html.tmpl
...ult/hook/search/search-report-select-rep_fields.html.tmpl
+21
-0
voting.html.tmpl
extensions/Voting/template/en/default/pages/voting.html.tmpl
+1
-1
bug.html.tmpl
...ons/Voting/template/en/default/pages/voting/bug.html.tmpl
+6
-5
user.html.tmpl
...ns/Voting/template/en/default/pages/voting/user.html.tmpl
+9
-9
delete-all.html.tmpl
...ns/Voting/template/en/default/voting/delete-all.html.tmpl
+1
-1
votes-removed.txt.tmpl
.../Voting/template/en/default/voting/votes-removed.txt.tmpl
+0
-0
style.css
extensions/Voting/web/style.css
+0
-0
importxml.pl
importxml.pl
+0
-1
process_bug.cgi
process_bug.cgi
+0
-15
query.cgi
query.cgi
+1
-1
report.cgi
report.cgi
+0
-1
sanitycheck.cgi
sanitycheck.cgi
+11
-97
show_bug.css
skins/standard/show_bug.css
+1
-2
email.html.tmpl
template/en/default/account/prefs/email.html.tmpl
+23
-32
bugfields.html.tmpl
template/en/default/admin/params/bugfields.html.tmpl
+0
-5
create.html.tmpl
template/en/default/admin/products/create.html.tmpl
+0
-3
edit-common.html.tmpl
template/en/default/admin/products/edit-common.html.tmpl
+2
-31
list.html.tmpl
template/en/default/admin/products/list.html.tmpl
+1
-16
updated.html.tmpl
template/en/default/admin/products/updated.html.tmpl
+2
-82
messages.html.tmpl
template/en/default/admin/sanitycheck/messages.html.tmpl
+0
-25
confirm-delete.html.tmpl
template/en/default/admin/users/confirm-delete.html.tmpl
+3
-20
edit.html.tmpl
template/en/default/bug/edit.html.tmpl
+2
-17
format_comment.txt.tmpl
template/en/default/bug/format_comment.txt.tmpl
+2
-2
header.html.tmpl
template/en/default/bug/process/header.html.tmpl
+2
-2
results.html.tmpl
template/en/default/bug/process/results.html.tmpl
+2
-1
newchangedmail.txt.tmpl
template/en/default/email/newchangedmail.txt.tmpl
+9
-28
filterexceptions.pl
template/en/default/filterexceptions.pl
+0
-16
field-descs.none.tmpl
template/en/default/global/field-descs.none.tmpl
+0
-1
reason-descs.none.tmpl
template/en/default/global/reason-descs.none.tmpl
+40
-0
site-navigation.html.tmpl
template/en/default/global/site-navigation.html.tmpl
+1
-6
user-error.html.tmpl
template/en/default/global/user-error.html.tmpl
+0
-39
list.rdf.tmpl
template/en/default/list/list.rdf.tmpl
+1
-1
form.html.tmpl
template/en/default/search/form.html.tmpl
+3
-20
search-help.html.tmpl
template/en/default/search/search-help.html.tmpl
+0
-3
search-report-select.html.tmpl
template/en/default/search/search-report-select.html.tmpl
+2
-2
sidebar.xul.tmpl
template/en/default/sidebar.xul.tmpl
+0
-3
votes.cgi
votes.cgi
+16
-330
No files found.
.bzrignore
View file @
120b63d5
...
...
@@ -27,6 +27,5 @@
/skins/contrib/Dusk/show_bug.css
/skins/contrib/Dusk/show_multiple.css
/skins/contrib/Dusk/summarize-time.css
/skins/contrib/Dusk/voting.css
/skins/contrib/Dusk/yui
.DS_Store
Bugzilla/Bug.pm
View file @
120b63d5
...
...
@@ -57,7 +57,6 @@ use URI::QueryParam;
use
base
qw(Bugzilla::Object Exporter)
;
@
Bugzilla::Bug::
EXPORT
=
qw(
bug_alias_to_id
RemoveVotes CheckIfVotedConfirmed
LogActivityEntry
editable_bug_fields
)
;
...
...
@@ -631,7 +630,6 @@ sub run_create_validators {
# You can't set these fields on bug creation (or sometimes ever).
delete
$params
->
{
resolution
};
delete
$params
->
{
votes
};
delete
$params
->
{
lastdiffed
};
delete
$params
->
{
bug_id
};
...
...
@@ -967,7 +965,6 @@ sub remove_from_db {
# - flags
# - keywords
# - longdescs
# - votes
# Also, the attach_data table uses attachments.attach_id as a foreign
# key, and so indirectly depends on a bug deletion too.
...
...
@@ -983,7 +980,6 @@ sub remove_from_db {
undef
,
(
$bug_id
,
$bug_id
));
$dbh
->
do
(
"DELETE FROM flags WHERE bug_id = ?"
,
undef
,
$bug_id
);
$dbh
->
do
(
"DELETE FROM keywords WHERE bug_id = ?"
,
undef
,
$bug_id
);
$dbh
->
do
(
"DELETE FROM votes WHERE bug_id = ?"
,
undef
,
$bug_id
);
# The attach_data table doesn't depend on bugs.bug_id directly.
my
$attach_ids
=
...
...
@@ -1819,7 +1815,7 @@ sub fields {
bug_status resolution dup_id see_also
bug_file_loc status_whiteboard keywords
priority bug_severity target_milestone
dependson blocked
votes
everconfirmed
dependson blocked everconfirmed
reporter assigned_to cc estimated_time
remaining_time actual_time deadline)
,
...
...
@@ -2870,14 +2866,6 @@ sub show_attachment_flags {
return
$self
->
{
'show_attachment_flags'
};
}
sub
use_votes
{
my
(
$self
)
=
@_
;
return
0
if
$self
->
{
'error'
};
return
Bugzilla
->
params
->
{
'usevotes'
}
&&
$self
->
product_obj
->
votes_per_user
>
0
;
}
sub
groups
{
my
$self
=
shift
;
return
$self
->
{
'groups'
}
if
exists
$self
->
{
'groups'
};
...
...
@@ -3019,20 +3007,6 @@ sub choices {
return
$self
->
{
'choices'
};
}
sub
votes
{
my
(
$self
)
=
@_
;
return
0
if
$self
->
{
error
};
return
$self
->
{
votes
}
if
defined
$self
->
{
votes
};
my
$dbh
=
Bugzilla
->
dbh
;
$self
->
{
votes
}
=
$dbh
->
selectrow_array
(
'SELECT SUM(vote_count) FROM votes
WHERE bug_id = ? '
.
$dbh
->
sql_group_by
(
'bug_id'
),
undef
,
$self
->
bug_id
);
$self
->
{
votes
}
||=
0
;
return
$self
->
{
votes
};
}
# Convenience Function. If you need speed, use this. If you need
# other Bug fields in addition to this, just create a new Bug with
# the alias.
...
...
@@ -3312,136 +3286,6 @@ sub CountOpenDependencies {
return
@dependencies
;
}
# If a bug is moved to a product which allows less votes per bug
# compared to the previous product, extra votes need to be removed.
sub
RemoveVotes
{
my
(
$id
,
$who
,
$reason
)
=
(
@_
);
my
$dbh
=
Bugzilla
->
dbh
;
my
$whopart
=
(
$who
)
?
" AND votes.who = $who"
:
""
;
my
$sth
=
$dbh
->
prepare
(
"SELECT profiles.login_name, "
.
"profiles.userid, votes.vote_count, "
.
"products.votesperuser, products.maxvotesperbug "
.
"FROM profiles "
.
"LEFT JOIN votes ON profiles.userid = votes.who "
.
"LEFT JOIN bugs ON votes.bug_id = bugs.bug_id "
.
"LEFT JOIN products ON products.id = bugs.product_id "
.
"WHERE votes.bug_id = ? "
.
$whopart
);
$sth
->
execute
(
$id
);
my
@list
;
while
(
my
(
$name
,
$userid
,
$oldvotes
,
$votesperuser
,
$maxvotesperbug
)
=
$sth
->
fetchrow_array
())
{
push
(
@list
,
[
$name
,
$userid
,
$oldvotes
,
$votesperuser
,
$maxvotesperbug
]);
}
# @messages stores all emails which have to be sent, if any.
# This array is passed to the caller which will send these emails itself.
my
@messages
=
();
if
(
scalar
(
@list
))
{
foreach
my
$ref
(
@list
)
{
my
(
$name
,
$userid
,
$oldvotes
,
$votesperuser
,
$maxvotesperbug
)
=
(
@$ref
);
$maxvotesperbug
=
min
(
$votesperuser
,
$maxvotesperbug
);
# If this product allows voting and the user's votes are in
# the acceptable range, then don't do anything.
next
if
$votesperuser
&&
$oldvotes
<=
$maxvotesperbug
;
# If the user has more votes on this bug than this product
# allows, then reduce the number of votes so it fits
my
$newvotes
=
$maxvotesperbug
;
my
$removedvotes
=
$oldvotes
-
$newvotes
;
if
(
$newvotes
)
{
$dbh
->
do
(
"UPDATE votes SET vote_count = ? "
.
"WHERE bug_id = ? AND who = ?"
,
undef
,
(
$newvotes
,
$id
,
$userid
));
}
else
{
$dbh
->
do
(
"DELETE FROM votes WHERE bug_id = ? AND who = ?"
,
undef
,
(
$id
,
$userid
));
}
# Notice that we did not make sure that the user fit within the $votesperuser
# range. This is considered to be an acceptable alternative to losing votes
# during product moves. Then next time the user attempts to change their votes,
# they will be forced to fit within the $votesperuser limit.
# Now lets send the e-mail to alert the user to the fact that their votes have
# been reduced or removed.
my
$vars
=
{
'to'
=>
$name
.
Bugzilla
->
params
->
{
'emailsuffix'
},
'bugid'
=>
$id
,
'reason'
=>
$reason
,
'votesremoved'
=>
$removedvotes
,
'votesold'
=>
$oldvotes
,
'votesnew'
=>
$newvotes
,
};
my
$voter
=
new
Bugzilla::
User
(
$userid
);
my
$template
=
Bugzilla
->
template_inner
(
$voter
->
settings
->
{
'lang'
}
->
{
'value'
});
my
$msg
;
$template
->
process
(
"email/votes-removed.txt.tmpl"
,
$vars
,
\
$msg
);
push
(
@messages
,
$msg
);
}
Bugzilla
->
template_inner
(
""
);
my
$votes
=
$dbh
->
selectrow_array
(
"SELECT SUM(vote_count) "
.
"FROM votes WHERE bug_id = ?"
,
undef
,
$id
)
||
0
;
$dbh
->
do
(
"UPDATE bugs SET votes = ? WHERE bug_id = ?"
,
undef
,
(
$votes
,
$id
));
}
# Now return the array containing emails to be sent.
return
@messages
;
}
# If a user votes for a bug, or the number of votes required to
# confirm a bug has been reduced, check if the bug is now confirmed.
sub
CheckIfVotedConfirmed
{
my
$id
=
shift
;
my
$bug
=
new
Bugzilla::
Bug
(
$id
);
my
$ret
=
0
;
if
(
!
$bug
->
everconfirmed
and
$bug
->
product_obj
->
votes_to_confirm
and
$bug
->
votes
>=
$bug
->
product_obj
->
votes_to_confirm
)
{
$bug
->
add_comment
(
''
,
{
type
=>
CMT_POPULAR_VOTES
});
if
(
$bug
->
bug_status
eq
'UNCONFIRMED'
)
{
# Get a valid open state.
my
$new_status
;
foreach
my
$state
(
@
{
$bug
->
status
->
can_change_to
})
{
if
(
$state
->
is_open
&&
$state
->
name
ne
'UNCONFIRMED'
)
{
$new_status
=
$state
->
name
;
last
;
}
}
ThrowCodeError
(
'no_open_bug_status'
)
unless
$new_status
;
# We cannot call $bug->set_status() here, because a user without
# canconfirm privs should still be able to confirm a bug by
# popular vote. We already know the new status is valid, so it's safe.
$bug
->
{
bug_status
}
=
$new_status
;
$bug
->
{
everconfirmed
}
=
1
;
delete
$bug
->
{
'status'
};
# Contains the status object.
}
else
{
# If the bug is in a closed state, only set everconfirmed to 1.
# Do not call $bug->_set_everconfirmed(), for the same reason as above.
$bug
->
{
everconfirmed
}
=
1
;
}
$bug
->
update
();
$ret
=
1
;
}
return
$ret
;
}
################################################################################
# check_can_change_field() defines what users are allowed to change. You
# can add code here for site-specific policy changes, according to the
...
...
Bugzilla/BugMail.pm
View file @
120b63d5
...
...
@@ -377,12 +377,6 @@ sub Send {
# the relationships in a hash. The keys are userids, the values are an
# array of role constants.
# Voters
my
$voters
=
$dbh
->
selectcol_arrayref
(
"SELECT who FROM votes WHERE bug_id = ?"
,
undef
,
(
$id
));
$recipients
{
$_
}
->
{
+
REL_VOTER
}
=
BIT_DIRECT
foreach
(
@$voters
);
# CCs
$recipients
{
$_
}
->
{
+
REL_CC
}
=
BIT_DIRECT
foreach
(
@ccs
);
...
...
@@ -405,8 +399,8 @@ sub Send {
foreach
my
$ref
(
@$diffs
)
{
my
(
$who
,
$whoname
,
$what
,
$when
,
$old
,
$new
)
=
(
@$ref
);
if
(
$old
)
{
# You can't stop being the reporter,
and mail isn't sent if you
# re
move your vot
e.
# You can't stop being the reporter,
so we don't check that
# re
lationship her
e.
# Ignore people whose user account has been deleted or renamed.
if
(
$what
eq
"CC"
)
{
foreach
my
$cc_user
(
split
(
/[\s,]+/
,
$old
))
{
...
...
@@ -462,7 +456,6 @@ sub Send {
foreach
my
$user_id
(
keys
%
recipients
)
{
my
%
rels_which_want
;
my
$sent_mail
=
0
;
my
$user
=
new
Bugzilla::
User
(
$user_id
);
# Deleted users must be excluded.
next
unless
$user
;
...
...
Bugzilla/Comment.pm
View file @
120b63d5
...
...
@@ -148,7 +148,7 @@ sub set_type {
sub
_check_extra_data
{
my
(
$invocant
,
$extra_data
,
$type
)
=
@_
;
$type
=
$invocant
->
type
if
ref
$invocant
;
if
(
$type
==
CMT_NORMAL
or
$type
==
CMT_POPULAR_VOTES
)
{
if
(
$type
==
CMT_NORMAL
)
{
if
(
defined
$extra_data
)
{
ThrowCodeError
(
'comment_extra_data_not_allowed'
,
{
type
=>
$type
,
extra_data
=>
$extra_data
});
...
...
Bugzilla/Config/BugFields.pm
View file @
120b63d5
...
...
@@ -72,12 +72,6 @@ sub get_param_list {
},
{
name
=>
'usevotes'
,
type
=>
'b'
,
default
=>
0
},
{
name
=>
'usebugaliases'
,
type
=>
'b'
,
default
=>
0
...
...
Bugzilla/Constants.pm
View file @
120b63d5
...
...
@@ -90,7 +90,6 @@ use File::Basename;
CMT_NORMAL
CMT_DUPE_OF
CMT_HAS_DUPE
CMT_POPULAR_VOTES
CMT_MOVED_TO
CMT_ATTACHMENT_CREATED
CMT_ATTACHMENT_UPDATED
...
...
@@ -98,7 +97,7 @@ use File::Basename;
THROW_ERROR
RELATIONSHIPS
REL_ASSIGNEE REL_QA REL_REPORTER REL_CC REL_
VOTER REL_
GLOBAL_WATCHER
REL_ASSIGNEE REL_QA REL_REPORTER REL_CC REL_GLOBAL_WATCHER
REL_ANY
POS_EVENTS
...
...
@@ -282,7 +281,7 @@ use constant MAX_COMMENT_LENGTH => 65535;
use
constant
CMT_NORMAL
=>
0
;
use
constant
CMT_DUPE_OF
=>
1
;
use
constant
CMT_HAS_DUPE
=>
2
;
use
constant
CMT_POPULAR_VOTES
=>
3
;
# Type 3 was CMT_POPULAR_VOTES, which moved to the Voting extension.
use
constant
CMT_MOVED_TO
=>
4
;
use
constant
CMT_ATTACHMENT_CREATED
=>
5
;
use
constant
CMT_ATTACHMENT_UPDATED
=>
6
;
...
...
@@ -295,7 +294,7 @@ use constant REL_ASSIGNEE => 0;
use
constant
REL_QA
=>
1
;
use
constant
REL_REPORTER
=>
2
;
use
constant
REL_CC
=>
3
;
use
constant
REL_VOTER
=>
4
;
# REL 4 was REL_VOTER, before it was moved ino an extension.
use
constant
REL_GLOBAL_WATCHER
=>
5
;
# We need these strings for the X-Bugzilla-Reasons header
...
...
@@ -307,7 +306,6 @@ use constant RELATIONSHIPS => {
REL_REPORTER
,
"Reporter"
,
REL_QA
,
"QAcontact"
,
REL_CC
,
"CC"
,
REL_VOTER
,
"Voter"
,
REL_GLOBAL_WATCHER
,
"GlobalWatcher"
};
...
...
Bugzilla/DB/Schema.pm
View file @
120b63d5
...
...
@@ -273,8 +273,6 @@ use constant ABSTRACT_SCHEMA => {
COLUMN
=>
'userid'
}},
status_whiteboard
=>
{
TYPE
=>
'MEDIUMTEXT'
,
NOTNULL
=>
1
,
DEFAULT
=>
"''"
},
votes
=>
{
TYPE
=>
'INT3'
,
NOTNULL
=>
1
,
DEFAULT
=>
'0'
},
# Note: keywords field is only a cache; the real data
# comes from the keywords table
keywords
=>
{
TYPE
=>
'MEDIUMTEXT'
,
NOTNULL
=>
1
,
...
...
@@ -309,7 +307,6 @@ use constant ABSTRACT_SCHEMA => {
bugs_resolution_idx
=>
[
'resolution'
],
bugs_target_milestone_idx
=>
[
'target_milestone'
],
bugs_qa_contact_idx
=>
[
'qa_contact'
],
bugs_votes_idx
=>
[
'votes'
],
],
},
...
...
@@ -434,24 +431,6 @@ use constant ABSTRACT_SCHEMA => {
],
},
votes
=>
{
FIELDS
=>
[
who
=>
{
TYPE
=>
'INT3'
,
NOTNULL
=>
1
,
REFERENCES
=>
{
TABLE
=>
'profiles'
,
COLUMN
=>
'userid'
,
DELETE
=>
'CASCADE'
}},
bug_id
=>
{
TYPE
=>
'INT3'
,
NOTNULL
=>
1
,
REFERENCES
=>
{
TABLE
=>
'bugs'
,
COLUMN
=>
'bug_id'
,
DELETE
=>
'CASCADE'
}},
vote_count
=>
{
TYPE
=>
'INT2'
,
NOTNULL
=>
1
},
],
INDEXES
=>
[
votes_who_idx
=>
[
'who'
],
votes_bug_id_idx
=>
[
'bug_id'
],
],
},
attachments
=>
{
FIELDS
=>
[
attach_id
=>
{
TYPE
=>
'MEDIUMSERIAL'
,
NOTNULL
=>
1
,
...
...
@@ -1223,12 +1202,6 @@ use constant ABSTRACT_SCHEMA => {
description
=>
{
TYPE
=>
'MEDIUMTEXT'
},
isactive
=>
{
TYPE
=>
'BOOLEAN'
,
NOTNULL
=>
1
,
DEFAULT
=>
1
},
votesperuser
=>
{
TYPE
=>
'INT2'
,
NOTNULL
=>
1
,
DEFAULT
=>
0
},
maxvotesperbug
=>
{
TYPE
=>
'INT2'
,
NOTNULL
=>
1
,
DEFAULT
=>
'10000'
},
votestoconfirm
=>
{
TYPE
=>
'INT2'
,
NOTNULL
=>
1
,
DEFAULT
=>
0
},
defaultmilestone
=>
{
TYPE
=>
'varchar(20)'
,
NOTNULL
=>
1
,
DEFAULT
=>
"'---'"
},
allows_unconfirmed
=>
{
TYPE
=>
'BOOLEAN'
,
NOTNULL
=>
1
,
...
...
Bugzilla/Field.pm
View file @
120b63d5
...
...
@@ -188,7 +188,6 @@ use constant DEFAULT_FIELDS => (
buglist
=>
1
},
{
name
=>
'reporter'
,
desc
=>
'ReportedBy'
,
in_new_bugmail
=>
1
,
buglist
=>
1
},
{
name
=>
'votes'
,
desc
=>
'Votes'
,
buglist
=>
1
},
{
name
=>
'qa_contact'
,
desc
=>
'QAContact'
,
in_new_bugmail
=>
1
,
buglist
=>
1
},
{
name
=>
'cc'
,
desc
=>
'CC'
,
in_new_bugmail
=>
1
},
...
...
Bugzilla/Install/DB.pm
View file @
120b63d5
...
...
@@ -168,11 +168,6 @@ sub update_table_definitions {
$dbh
->
bz_add_column
(
'bugs'
,
'everconfirmed'
,
{
TYPE
=>
'BOOLEAN'
,
NOTNULL
=>
1
},
1
);
$dbh
->
bz_add_column
(
'products'
,
'maxvotesperbug'
,
{
TYPE
=>
'INT2'
,
NOTNULL
=>
1
,
DEFAULT
=>
'10000'
});
$dbh
->
bz_add_column
(
'products'
,
'votestoconfirm'
,
{
TYPE
=>
'INT2'
,
NOTNULL
=>
1
},
0
);
_populate_milestones_table
();
# 2000-03-22 Changed the default value for target_milestone to be "---"
...
...
@@ -363,8 +358,10 @@ sub update_table_definitions {
{
TYPE
=>
'MEDIUMTEXT'
,
NOTNULL
=>
1
,
DEFAULT
=>
"''"
});
$dbh
->
bz_alter_column
(
'bugs'
,
'keywords'
,
{
TYPE
=>
'MEDIUMTEXT'
,
NOTNULL
=>
1
,
DEFAULT
=>
"''"
});
$dbh
->
bz_alter_column
(
'bugs'
,
'votes'
,
{
TYPE
=>
'INT3'
,
NOTNULL
=>
1
,
DEFAULT
=>
'0'
});
if
(
$dbh
->
bz_column_info
(
'bugs'
,
'votes'
))
{
$dbh
->
bz_alter_column
(
'bugs'
,
'votes'
,
{
TYPE
=>
'INT3'
,
NOTNULL
=>
1
,
DEFAULT
=>
'0'
});
}
$dbh
->
bz_alter_column
(
'bugs'
,
'lastdiffed'
,
{
TYPE
=>
'DATETIME'
});
...
...
@@ -469,11 +466,14 @@ sub update_table_definitions {
if
(
$dbh
->
bz_column_info
(
'products'
,
'disallownew'
)){
$dbh
->
bz_alter_column
(
'products'
,
'disallownew'
,
{
TYPE
=>
'BOOLEAN'
,
NOTNULL
=>
1
,
DEFAULT
=>
0
});
if
(
$dbh
->
bz_column_info
(
'products'
,
'votesperuser'
))
{
$dbh
->
bz_alter_column
(
'products'
,
'votesperuser'
,
{
TYPE
=>
'INT2'
,
NOTNULL
=>
1
,
DEFAULT
=>
0
});
$dbh
->
bz_alter_column
(
'products'
,
'votestoconfirm'
,
{
TYPE
=>
'INT2'
,
NOTNULL
=>
1
,
DEFAULT
=>
0
});
}
}
$dbh
->
bz_alter_column
(
'products'
,
'votesperuser'
,
{
TYPE
=>
'INT2'
,
NOTNULL
=>
1
,
DEFAULT
=>
0
});
$dbh
->
bz_alter_column
(
'products'
,
'votestoconfirm'
,
{
TYPE
=>
'INT2'
,
NOTNULL
=>
1
,
DEFAULT
=>
0
});
# 2006-08-04 LpSolit@gmail.com - Bug 305941
$dbh
->
bz_drop_column
(
'profiles'
,
'refreshed_when'
);
...
...
@@ -654,14 +654,14 @@ sub _add_bug_vote_cache {
# (P.S. All is not lost; it appears that the latest betas of MySQL
# support a new table format which will allow 32 indices.)
$dbh
->
bz_drop_column
(
'bugs'
,
'area'
);
if
(
!
$dbh
->
bz_column_info
(
'bugs'
,
'votes'
))
{
if
(
$dbh
->
bz_column_info
(
'bugs'
,
'area'
))
{
$dbh
->
bz_drop_column
(
'bugs'
,
'area'
);
$dbh
->
bz_add_column
(
'bugs'
,
'votes'
,
{
TYPE
=>
'INT3'
,
NOTNULL
=>
1
,
DEFAULT
=>
0
});
$dbh
->
bz_add_index
(
'bugs'
,
'bugs_votes_idx'
,
[
qw(votes)
]);
$dbh
->
bz_add_column
(
'products'
,
'votesperuser'
,
{
TYPE
=>
'INT2'
,
NOTNULL
=>
1
},
0
);
}
$dbh
->
bz_add_column
(
'products'
,
'votesperuser'
,
{
TYPE
=>
'INT2'
,
NOTNULL
=>
1
},
0
);
}
sub
_update_product_name_definition
{
...
...
@@ -896,9 +896,11 @@ sub _add_unique_login_name_index_to_profiles {
[
"votes"
,
"who"
],
[
"longdescs"
,
"who"
])
{
my
(
$table
,
$field
)
=
(
@$i
);
print
" Updating $table.$field...\n"
;
$dbh
->
do
(
"UPDATE $table SET $field = $u1 "
.
"WHERE $field = $u2"
);
if
(
$dbh
->
bz_table_info
(
$table
))
{
print
" Updating $table.$field...\n"
;
$dbh
->
do
(
"UPDATE $table SET $field = $u1 "
.
"WHERE $field = $u2"
);
}
}
$dbh
->
do
(
"DELETE FROM profiles WHERE userid = $u2"
);
}
...
...
@@ -2206,9 +2208,9 @@ sub _rename_votes_count_and_force_group_refresh {
#
# Renaming the 'count' column in the votes table because Sybase doesn't
# like it
if
(
$dbh
->
bz_column_info
(
'votes'
,
'count'
))
{
$dbh
->
bz_rename_column
(
'votes'
,
'count'
,
'vote_
count'
);
}
return
if
!
$dbh
->
bz_table_info
(
'votes'
);
return
if
$dbh
->
bz_column_info
(
'votes'
,
'
count'
);
$dbh
->
bz_rename_column
(
'votes'
,
'count'
,
'vote_count'
);
}
sub
_fix_group_with_empty_name
{
...
...
@@ -2266,7 +2268,9 @@ sub _migrate_email_prefs_to_new_table {
"Reporter"
=>
REL_REPORTER
,
"QAcontact"
=>
REL_QA
,
"CClist"
=>
REL_CC
,
"Voter"
=>
REL_VOTER
);
# REL_VOTER was "4" before it was moved to an
# extension.
"Voter"
=>
4
);
my
%
events
=
(
"Removeme"
=>
EVT_ADDED_REMOVED
,
"Comments"
=>
EVT_COMMENT
,
...
...
@@ -3343,8 +3347,10 @@ sub _add_allows_unconfirmed_to_product_table {
if
(
!
$dbh
->
bz_column_info
(
'products'
,
'allows_unconfirmed'
))
{
$dbh
->
bz_add_column
(
'products'
,
'allows_unconfirmed'
,
{
TYPE
=>
'BOOLEAN'
,
NOTNULL
=>
1
,
DEFAULT
=>
'FALSE'
});
$dbh
->
do
(
'UPDATE products SET allows_unconfirmed = 1
WHERE votestoconfirm > 0'
);
if
(
$dbh
->
bz_column_info
(
'products'
,
'votestoconfirm'
))
{
$dbh
->
do
(
'UPDATE products SET allows_unconfirmed = 1
WHERE votestoconfirm > 0'
);
}
}
}
...
...
Bugzilla/Object.pm
View file @
120b63d5
...
...
@@ -278,7 +278,8 @@ sub set {
my
(
$self
,
$field
,
$value
)
=
@_
;
# This method is protected. It's used to help implement set_ functions.
caller
->
isa
(
'Bugzilla::Object'
)
my
$caller
=
caller
;
$caller
->
isa
(
'Bugzilla::Object'
)
||
$caller
->
isa
(
'Bugzilla::Extension'
)
||
ThrowCodeError
(
'protection_violation'
,
{
caller
=>
caller
,
superclass
=>
__PACKAGE__
,
...
...
Bugzilla/Product.pm
View file @
120b63d5
...
...
@@ -48,9 +48,6 @@ use constant DB_COLUMNS => qw(
classification_id
description
isactive
votesperuser
maxvotesperbug
votestoconfirm
defaultmilestone
allows_unconfirmed
)
;
...
...
@@ -66,9 +63,6 @@ use constant UPDATE_COLUMNS => qw(
description
defaultmilestone
isactive
votesperuser
maxvotesperbug
votestoconfirm
allows_unconfirmed
)
;
...
...
@@ -80,9 +74,6 @@ use constant VALIDATORS => {
version
=>
\&
_check_version
,
defaultmilestone
=>
\&
_check_default_milestone
,
isactive
=>
\&
Bugzilla::Object::
check_boolean
,
votesperuser
=>
\&
_check_votes_per_user
,
maxvotesperbug
=>
\&
_check_votes_per_bug
,
votestoconfirm
=>
\&
_check_votes_to_confirm
,
create_series
=>
\&
Bugzilla::Object::
check_boolean
};
...
...
@@ -155,99 +146,6 @@ sub update {
$dbh
->
bz_start_transaction
();
my
(
$changes
,
$old_self
)
=
$self
->
SUPER::
update
(
@_
);
# We also have to fix votes.
my
@msgs
;
# Will store emails to send to voters.
if
(
$changes
->
{
maxvotesperbug
}
||
$changes
->
{
votesperuser
}
||
$changes
->
{
votestoconfirm
})
{
# We cannot |use| these modules, due to dependency loops.
require
Bugzilla::
Bug
;
import
Bugzilla::
Bug
qw(RemoveVotes CheckIfVotedConfirmed)
;
require
Bugzilla::
User
;
import
Bugzilla::
User
qw(user_id_to_login)
;
# 1. too many votes for a single user on a single bug.
my
@toomanyvotes_list
=
();
if
(
$self
->
max_votes_per_bug
<
$self
->
votes_per_user
)
{
my
$votes
=
$dbh
->
selectall_arrayref
(
'SELECT votes.who, votes.bug_id
FROM votes
INNER JOIN bugs
ON bugs.bug_id = votes.bug_id
WHERE bugs.product_id = ?
AND votes.vote_count > ?'
,
undef
,
(
$self
->
id
,
$self
->
max_votes_per_bug
));
foreach
my
$vote
(
@$votes
)
{
my
(
$who
,
$id
)
=
(
@$vote
);
# If some votes are removed, RemoveVotes() returns a list
# of messages to send to voters.
push
(
@msgs
,
RemoveVotes
(
$id
,
$who
,
'votes_too_many_per_bug'
));
my
$name
=
user_id_to_login
(
$who
);
push
(
@toomanyvotes_list
,
{
id
=>
$id
,
name
=>
$name
});
}
}
$changes
->
{
'too_many_votes'
}
=
\
@toomanyvotes_list
;
# 2. too many total votes for a single user.
# This part doesn't work in the general case because RemoveVotes
# doesn't enforce votesperuser (except per-bug when it's less
# than maxvotesperbug). See Bugzilla::Bug::RemoveVotes().
my
$votes
=
$dbh
->
selectall_arrayref
(
'SELECT votes.who, votes.vote_count
FROM votes
INNER JOIN bugs
ON bugs.bug_id = votes.bug_id
WHERE bugs.product_id = ?'
,
undef
,
$self
->
id
);
my
%
counts
;
foreach
my
$vote
(
@$votes
)
{
my
(
$who
,
$count
)
=
@$vote
;
if
(
!
defined
$counts
{
$who
})
{
$counts
{
$who
}
=
$count
;
}
else
{
$counts
{
$who
}
+=
$count
;
}
}
my
@toomanytotalvotes_list
=
();
foreach
my
$who
(
keys
(
%
counts
))
{
if
(
$counts
{
$who
}
>
$self
->
votes_per_user
)
{
my
$bug_ids
=
$dbh
->
selectcol_arrayref
(
'SELECT votes.bug_id
FROM votes
INNER JOIN bugs
ON bugs.bug_id = votes.bug_id
WHERE bugs.product_id = ?
AND votes.who = ?'
,
undef
,
(
$self
->
id
,
$who
));
foreach
my
$bug_id
(
@$bug_ids
)
{
# RemoveVotes() returns a list of messages to send
# in case some voters had too many votes.
push
(
@msgs
,
RemoveVotes
(
$bug_id
,
$who
,
'votes_too_many_per_user'
));
my
$name
=
user_id_to_login
(
$who
);
push
(
@toomanytotalvotes_list
,
{
id
=>
$bug_id
,
name
=>
$name
});
}
}
}
$changes
->
{
'too_many_total_votes'
}
=
\
@toomanytotalvotes_list
;
# 3. enough votes to confirm
my
$bug_list
=
$dbh
->
selectcol_arrayref
(
'SELECT bug_id FROM bugs WHERE product_id = ?
AND bug_status = ? AND votes >= ?'
,
undef
,
(
$self
->
id
,
'UNCONFIRMED'
,
$self
->
votes_to_confirm
));
my
@updated_bugs
=
();
foreach
my
$bug_id
(
@$bug_list
)
{
my
$confirmed
=
CheckIfVotedConfirmed
(
$bug_id
);
push
(
@updated_bugs
,
$bug_id
)
if
$confirmed
;
}
$changes
->
{
'confirmed_bugs'
}
=
\
@updated_bugs
;
}
# Also update group settings.
if
(
$self
->
{
check_group_controls
})
{
require
Bugzilla::
Bug
;
...
...
@@ -364,11 +262,6 @@ sub update {
delete
$self
->
{
check_group_controls
};
Bugzilla
->
user
->
clear_product_cache
();
# Now that changes have been committed, we can send emails to voters.
foreach
my
$msg
(
@msgs
)
{
MessageToMTA
(
$msg
);
}
return
$changes
;
}
...
...
@@ -524,37 +417,6 @@ sub _check_milestone_url {
return
$url
;
}
sub
_check_votes_per_user
{
return
_check_votes
(
@_
,
0
);
}
sub
_check_votes_per_bug
{
return
_check_votes
(
@_
,
10000
);
}
sub
_check_votes_to_confirm
{
return
_check_votes
(
@_
,
0
);
}
# This subroutine is only used internally by other _check_votes_* validators.
sub
_check_votes
{
my
(
$invocant
,
$votes
,
$field
,
$default
)
=
@_
;
detaint_natural
(
$votes
);
# On product creation, if the number of votes is not a valid integer,
# we silently fall back to the given default value.
# If the product already exists and the change is illegal, we complain.
if
(
!
defined
$votes
)
{
if
(
ref
$invocant
)
{
ThrowUserError
(
'product_illegal_votes'
,
{
field
=>
$field
,
votes
=>
$_
[
1
]});
}
else
{
$votes
=
$default
;
}
}
return
$votes
;
}
#####################################
# Implement Bugzilla::Field::Choice #
#####################################
...
...
@@ -618,9 +480,6 @@ sub set_name { $_[0]->set('name', $_[1]); }
sub
set_description
{
$_
[
0
]
->
set
(
'description'
,
$_
[
1
]);
}
sub
set_default_milestone
{
$_
[
0
]
->
set
(
'defaultmilestone'
,
$_
[
1
]);
}
sub
set_is_active
{
$_
[
0
]
->
set
(
'isactive'
,
$_
[
1
]);
}
sub
set_votes_per_user
{
$_
[
0
]
->
set
(
'votesperuser'
,
$_
[
1
]);
}
sub
set_votes_per_bug
{
$_
[
0
]
->
set
(
'maxvotesperbug'
,
$_
[
1
]);
}
sub
set_votes_to_confirm
{
$_
[
0
]
->
set
(
'votestoconfirm'
,
$_
[
1
]);
}
sub
set_allows_unconfirmed
{
$_
[
0
]
->
set
(
'allows_unconfirmed'
,
$_
[
1
]);
}
sub
set_group_controls
{
...
...
@@ -876,9 +735,6 @@ sub flag_types {
sub
allows_unconfirmed
{
return
$_
[
0
]
->
{
'allows_unconfirmed'
};
}
sub
description
{
return
$_
[
0
]
->
{
'description'
};
}
sub
is_active
{
return
$_
[
0
]
->
{
'isactive'
};
}
sub
votes_per_user
{
return
$_
[
0
]
->
{
'votesperuser'
};
}
sub
max_votes_per_bug
{
return
$_
[
0
]
->
{
'maxvotesperbug'
};
}
sub
votes_to_confirm
{
return
$_
[
0
]
->
{
'votestoconfirm'
};
}
sub
default_milestone
{
return
$_
[
0
]
->
{
'defaultmilestone'
};
}
sub
classification_id
{
return
$_
[
0
]
->
{
'classification_id'
};
}
...
...
@@ -939,9 +795,6 @@ Bugzilla::Product - Bugzilla product class.
my $name = $product->name;
my $description = $product->description;
my isactive = $product->is_active;
my votesperuser = $product->votes_per_user;
my maxvotesperbug = $product->max_votes_per_bug;
my votestoconfirm = $product->votes_to_confirm;
my $defaultmilestone = $product->default_milestone;
my $classificationid = $product->classification_id;
my $allows_unconfirmed = $product->allows_unconfirmed;
...
...
Bugzilla/Search.pm
View file @
120b63d5
...
...
@@ -86,10 +86,8 @@ use constant SPECIAL_ORDER_JOIN => {
# 3. title: The title of the column as displayed to users.
#
# Note: There are a few hacks in the code that deviate from these definitions.
# In particular, when the list is sorted by the "votes" field the word
# "DESC" is added to the end of the field to sort in descending order,
# and the redundant short_desc column is removed when the client
# requests "all" columns.
# In particular, the redundant short_desc column is removed when the
# client requests "all" columns.
#
# This is really a constant--that is, once it's been called once, the value
# will always be the same unless somebody adds a new custom field. But
...
...
@@ -281,18 +279,6 @@ sub init {
push
(
@supptables
,
"LEFT JOIN flagtypes ON flagtypes.id = flags.type_id"
);
}
my
$minvotes
;
if
(
defined
$params
->
param
(
'votes'
))
{
my
$c
=
trim
(
$params
->
param
(
'votes'
));
if
(
$c
ne
""
)
{
if
(
$c
!~
/^[0-9]*$/
)
{
ThrowUserError
(
"illegal_at_least_x_votes"
,
{
value
=>
$c
});
}
push
(
@specialchart
,
[
"votes"
,
"greaterthan"
,
$c
-
1
]);
}
}
# If the user has selected all of either status or resolution, change to
# selecting none. This is functionally equivalent, but quite a lot faster.
# Also, if the status is __open__ or __closed__, translate those
...
...
Bugzilla/Search/Quicksearch.pm
View file @
120b63d5
...
...
@@ -339,12 +339,6 @@ sub _handle_special_first_chars {
sub
_handle_field_names
{
my
(
$or_operand
,
$negate
,
$unknownFields
,
$ambiguous_fields
)
=
@_
;
# votes:xx ("at least xx votes")
if
(
$or_operand
=~
/^votes:([0-9]+)$/
)
{
addChart
(
'votes'
,
'greaterthan'
,
$1
-
1
,
$negate
);
return
1
;
}
# Flag and requestee shortcut
if
(
$or_operand
=~
/^(?:flag:)?([^\?]+\?)([^\?]*)$/
)
{
addChart
(
'flagtypes.name'
,
'substring'
,
$1
,
$negate
);
...
...
@@ -454,18 +448,6 @@ sub _special_field_syntax {
return
1
;
}
# Votes (votes>xx)
if
(
$word
=~
m/^votes>([0-9]+)$/
)
{
addChart
(
'votes'
,
'greaterthan'
,
$1
,
$negate
);
return
1
;
}
# Votes (votes>=xx, votes=>xx)
if
(
$word
=~
m/^votes(>=|=>)([0-9]+)$/
)
{
addChart
(
'votes'
,
'greaterthan'
,
$2
-
1
,
$negate
);
return
1
;
}
return
0
;
}
...
...
Bugzilla/WebService/Bug.pm
View file @
120b63d5
...
...
@@ -386,9 +386,6 @@ sub search {
if
(
my
$when
=
delete
$params
->
{
creation_ts
})
{
$params
->
{
WHERE
}
->
{
'creation_ts >= ?'
}
=
$when
;
}
if
(
my
$votes
=
delete
$params
->
{
votes
})
{
$params
->
{
WHERE
}
->
{
'votes >= ?'
}
=
$votes
;
}
if
(
my
$summary
=
delete
$params
->
{
short_desc
})
{
my
@strings
=
ref
$summary
?
@$summary
:
(
$summary
);
my
@likes
=
(
"short_desc LIKE ?"
)
x
@strings
;
...
...
@@ -1687,11 +1684,6 @@ C<string> The "URL" field of a bug.
C<string> The Version field of a bug.
=item C<votes>
C<int> Searches for bugs with this many votes or greater. May not
be an array.
=item C<whiteboard>
C<string> Search the "Status Whiteboard" field on bugs for a substring.
...
...
@@ -1722,6 +1714,8 @@ for that value.
=item Added in Bugzilla B<3.4>.
=item Searching by C<votes> was removed in Bugzilla B<3.8>.
=back
=back
...
...
buglist.cgi
View file @
120b63d5
...
...
@@ -653,18 +653,6 @@ else {
# and are hard-coded into the display templates.
@displaycolumns
=
grep
(
$_
ne
'bug_id'
,
@displaycolumns
);
# Add the votes column to the list of columns to be displayed
# in the bug list if the user is searching for bugs with a certain
# number of votes and the votes column is not already on the list.
# Some versions of perl will taint 'votes' if this is done as a single
# statement, because the votes param is tainted at this point
my
$votes
=
$params
->
param
(
'votes'
);
$votes
||=
""
;
if
(
trim
(
$votes
)
&&
!
grep
(
$_
eq
'votes'
,
@displaycolumns
))
{
push
(
@displaycolumns
,
'votes'
);
}
# Remove the timetracking columns if they are not a part of the group
# (happens if a user had access to time tracking and it was revoked/disabled)
if
(
!
Bugzilla
->
user
->
is_timetracker
)
{
...
...
@@ -806,12 +794,6 @@ if ($order) {
# Special handlings for certain columns
next
if
$column_name
eq
'relevance'
&&
!
$fulltext
;
# If we are sorting by votes, sort in descending order if
# no explicit sort order was given.
if
(
$column_name
eq
'votes'
&&
!
$direction
)
{
$direction
=
"DESC"
;
}
if
(
exists
$columns
->
{
$column_name
})
{
$direction
=
" $direction"
if
$direction
;
push
(
@order
,
"$column_name$direction"
);
...
...
bugzilla.dtd
View file @
120b63d5
...
...
@@ -5,7 +5,7 @@
maintainer CDATA #REQUIRED
exporter CDATA #IMPLIED
>
<!ELEMENT bug (bug_id, (alias?, creation_ts, short_desc, delta_ts, reporter_accessible, cclist_accessible, classification_id, classification, product, component, version, rep_platform, op_sys, bug_status, resolution?, dup_id?, bug_file_loc?, status_whiteboard?, keywords*, priority, bug_severity, target_milestone?, dependson*, blocked*,
votes?,
everconfirmed, reporter, assigned_to, qa_contact?, cc*, (estimated_time, remaining_time, actual_time, deadline)?, group*, flag*, long_desc*, attachment*)?)>
<!ELEMENT bug (bug_id, (alias?, creation_ts, short_desc, delta_ts, reporter_accessible, cclist_accessible, classification_id, classification, product, component, version, rep_platform, op_sys, bug_status, resolution?, dup_id?, bug_file_loc?, status_whiteboard?, keywords*, priority, bug_severity, target_milestone?, dependson*, blocked*, everconfirmed, reporter, assigned_to, qa_contact?, cc*, (estimated_time, remaining_time, actual_time, deadline)?, group*, flag*, long_desc*, attachment*)?)>
<!ATTLIST bug
error (NotFound | NotPermitted | InvalidBugId) #IMPLIED
>
...
...
@@ -39,7 +39,6 @@
<!ELEMENT keywords (#PCDATA)>
<!ELEMENT dependson (#PCDATA)>
<!ELEMENT blocked (#PCDATA)>
<!ELEMENT votes (#PCDATA)>
<!ELEMENT everconfirmed (#PCDATA)>
<!ELEMENT cc (#PCDATA)>
<!ELEMENT group (#PCDATA)>
...
...
colchange.cgi
View file @
120b63d5
...
...
@@ -55,9 +55,6 @@ if (Bugzilla->params->{"useclassification"}) {
push
(
@masterlist
,
(
"product"
,
"component"
,
"version"
,
"op_sys"
));
if
(
Bugzilla
->
params
->
{
"usevotes"
})
{
push
(
@masterlist
,
"votes"
);
}
if
(
Bugzilla
->
params
->
{
"usebugaliases"
})
{
unshift
(
@masterlist
,
"alias"
);
}
...
...
contrib/merge-users.pl
View file @
120b63d5
...
...
@@ -129,7 +129,6 @@ my $changes = {
flags
=>
[
'setter_id'
,
'requestee_id'
],
cc
=>
[
'who bug_id'
],
longdescs
=>
[
'who'
],
votes
=>
[
'who'
],
# Tables affecting global behavior / other users.
components
=>
[
'initialowner'
,
'initialqacontact'
],
component_cc
=>
[
'user_id component_id'
],
...
...
docs/en/images/bzLifecycle.xml
View file @
120b63d5
...
...
@@ -968,8 +968,7 @@ UNCONFIRMED state#</dia:string>
<dia:attribute
name=
"text"
>
<dia:composite
type=
"text"
>
<dia:attribute
name=
"string"
>
<dia:string>
#Bug confirmed or
receives enough votes#
</dia:string>
<dia:string>
#Bug confirmed#
</dia:string>
</dia:attribute>
<dia:attribute
name=
"font"
>
<dia:font
family=
"sans"
style=
"0"
name=
"Helvetica"
/>
...
...
editproducts.cgi
View file @
120b63d5
...
...
@@ -186,11 +186,6 @@ if ($action eq 'new') {
create_series
=>
scalar
$cgi
->
param
(
'createseries'
),
allows_unconfirmed
=>
scalar
$cgi
->
param
(
'allows_unconfirmed'
),
);
if
(
Bugzilla
->
params
->
{
'usevotes'
})
{
$create_params
{
votesperuser
}
=
$cgi
->
param
(
'votesperuser'
);
$create_params
{
maxvotesperbug
}
=
$cgi
->
param
(
'maxvotesperbug'
);
$create_params
{
votestoconfirm
}
=
$cgi
->
param
(
'votestoconfirm'
);
}
my
$product
=
Bugzilla::
Product
->
create
(
\%
create_params
);
delete_token
(
$token
);
...
...
@@ -295,16 +290,13 @@ if ($action eq 'update') {
my
$product_old_name
=
trim
(
$cgi
->
param
(
'product_old_name'
)
||
''
);
my
$product
=
$user
->
check_can_admin_product
(
$product_old_name
);
$product
->
set_name
(
$product_name
);
$product
->
set_description
(
scalar
$cgi
->
param
(
'description'
));
$product
->
set_default_milestone
(
scalar
$cgi
->
param
(
'defaultmilestone'
));
$product
->
set_is_active
(
scalar
$cgi
->
param
(
'is_active'
));
if
(
Bugzilla
->
params
->
{
'usevotes'
})
{
$product
->
set_votes_per_user
(
scalar
$cgi
->
param
(
'votesperuser'
));
$product
->
set_votes_per_bug
(
scalar
$cgi
->
param
(
'maxvotesperbug'
));
$product
->
set_votes_to_confirm
(
scalar
$cgi
->
param
(
'votestoconfirm'
));
}
$product
->
set_allows_unconfirmed
(
scalar
$cgi
->
param
(
'allows_unconfirmed'
));
$product
->
set_all
({
name
=>
$product_name
,
description
=>
scalar
$cgi
->
param
(
'description'
),
is_active
=>
scalar
$cgi
->
param
(
'is_active'
),
allows_unconfirmed
=>
scalar
$cgi
->
param
(
'allows_unconfirmed'
),
default_milestone
=>
scalar
$cgi
->
param
(
'defaultmilestone'
),
});
my
$changes
=
$product
->
update
();
...
...
editusers.cgi
View file @
120b63d5
...
...
@@ -424,9 +424,6 @@ if ($action eq 'search') {
$vars
->
{
'series'
}
=
$dbh
->
selectrow_array
(
'SELECT COUNT(*) FROM series WHERE creator = ?'
,
undef
,
$otherUserID
);
$vars
->
{
'votes'
}
=
$dbh
->
selectrow_array
(
'SELECT COUNT(*) FROM votes WHERE who = ?'
,
undef
,
$otherUserID
);
$vars
->
{
'watch'
}{
'watched'
}
=
$dbh
->
selectrow_array
(
'SELECT COUNT(*) FROM watch WHERE watched = ?'
,
undef
,
$otherUserID
);
...
...
@@ -537,7 +534,6 @@ if ($action eq 'search') {
$dbh
->
do
(
'DELETE FROM tokens WHERE userid = ?'
,
undef
,
$otherUserID
);
$dbh
->
do
(
'DELETE FROM user_group_map WHERE user_id = ?'
,
undef
,
$otherUserID
);
$dbh
->
do
(
'DELETE FROM votes WHERE who = ?'
,
undef
,
$otherUserID
);
$dbh
->
do
(
'DELETE FROM watch WHERE watcher = ? OR watched = ?'
,
undef
,
(
$otherUserID
,
$otherUserID
));
...
...
extensions/Voting/Extension.pm
0 → 100644
View file @
120b63d5
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# The Initial Developer of the Original Code is Netscape Communications
# Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All
# Rights Reserved.
#
# Contributor(s): Terry Weissman <terry@mozilla.org>
# Stephan Niemz <st.n@gmx.net>
# Christopher Aillon <christopher@aillon.com>
# Gervase Markham <gerv@gerv.net>
# Frédéric Buclin <LpSolit@gmail.com>
# Max Kanat-Alexander <mkanat@bugzilla.org>
package
Bugzilla::Extension::
Voting
;
use
strict
;
use
base
qw(Bugzilla::Extension)
;
use
Bugzilla::
Bug
;
use
Bugzilla::
Constants
;
use
Bugzilla::
Error
;
use
Bugzilla::
Field
;
use
Bugzilla::
Mailer
;
use
Bugzilla::
User
;
use
Bugzilla::
Util
qw(detaint_natural)
;
use
List::
Util
qw(min)
;
use
constant
NAME
=>
'Voting'
;
use
constant
DEFAULT_VOTES_PER_BUG
=>
1
;
# These came from Bugzilla itself, so they maintain the old numbers
# they had before.
use
constant
CMT_POPULAR_VOTES
=>
3
;
use
constant
REL_VOTER
=>
4
;
################
# Installation #
################
our
$VERSION
=
BUGZILLA_VERSION
;
sub
db_schema_abstract_schema
{
my
(
$self
,
$args
)
=
@_
;
$args
->
{
'schema'
}
->
{
'votes'
}
=
{
FIELDS
=>
[
who
=>
{
TYPE
=>
'INT3'
,
NOTNULL
=>
1
,
REFERENCES
=>
{
TABLE
=>
'profiles'
,
COLUMN
=>
'userid'
,
DELETE
=>
'CASCADE'
}},
bug_id
=>
{
TYPE
=>
'INT3'
,
NOTNULL
=>
1
,
REFERENCES
=>
{
TABLE
=>
'bugs'
,
COLUMN
=>
'bug_id'
,
DELETE
=>
'CASCADE'
}},
vote_count
=>
{
TYPE
=>
'INT2'
,
NOTNULL
=>
1
},
],
INDEXES
=>
[
votes_who_idx
=>
[
'who'
],
votes_bug_id_idx
=>
[
'bug_id'
],
],
};
}
sub
install_update_db
{
my
$dbh
=
Bugzilla
->
dbh
;
# Note that before Bugzilla 3.8, voting was a built-in part of Bugzilla,
# so updates to the columns for old versions of Bugzilla happen in
# Bugzilla::Install::DB, and can't safely be moved to this extension.
my
$field
=
new
Bugzilla::
Field
({
name
=>
'votes'
});
if
(
!
$field
)
{
Bugzilla::
Field
->
create
(
{
name
=>
'votes'
,
description
=>
'Votes'
,
buglist
=>
1
});
}
$dbh
->
bz_add_column
(
'products'
,
'votesperuser'
,
{
TYPE
=>
'INT2'
,
NOTNULL
=>
1
,
DEFAULT
=>
0
});
$dbh
->
bz_add_column
(
'products'
,
'maxvotesperbug'
,
{
TYPE
=>
'INT2'
,
NOTNULL
=>
1
,
DEFAULT
=>
DEFAULT_VOTES_PER_BUG
});
$dbh
->
bz_add_column
(
'products'
,
'votestoconfirm'
,
{
TYPE
=>
'INT2'
,
NOTNULL
=>
1
,
DEFAULT
=>
0
});
$dbh
->
bz_add_column
(
'bugs'
,
'votes'
,
{
TYPE
=>
'INT3'
,
NOTNULL
=>
1
,
DEFAULT
=>
0
});
$dbh
->
bz_add_index
(
'bugs'
,
'bugs_votes_idx'
,
[
'votes'
]);
# maxvotesperbug used to default to 10,000, which isn't very sensible.
my
$per_bug
=
$dbh
->
bz_column_info
(
'products'
,
'maxvotesperbug'
);
if
(
$per_bug
->
{
DEFAULT
}
!=
DEFAULT_VOTES_PER_BUG
)
{
$dbh
->
bz_alter_column
(
'products'
,
'maxvotesperbug'
,
{
TYPE
=>
'INT2'
,
NOTNULL
=>
1
,
DEFAULT
=>
DEFAULT_VOTES_PER_BUG
});
}
}
###########
# Objects #
###########
sub
object_columns
{
my
(
$self
,
$args
)
=
@_
;
my
(
$class
,
$columns
)
=
@$args
{
qw(class columns)
};
if
(
$class
->
isa
(
'Bugzilla::Bug'
))
{
push
(
@$columns
,
'votes'
);
}
elsif
(
$class
->
isa
(
'Bugzilla::Product'
))
{
push
(
@$columns
,
qw(votesperuser maxvotesperbug votestoconfirm)
);
}
}
sub
bug_fields
{
my
(
$self
,
$args
)
=
@_
;
my
$fields
=
$args
->
{
fields
};
push
(
@$fields
,
'votes'
);
}
sub
object_update_columns
{
my
(
$self
,
$args
)
=
@_
;
my
(
$object
,
$columns
)
=
@$args
{
qw(object columns)
};
if
(
$object
->
isa
(
'Bugzilla::Product'
))
{
push
(
@$columns
,
qw(votesperuser maxvotesperbug votestoconfirm)
);
}
}
sub
object_validators
{
my
(
$self
,
$args
)
=
@_
;
my
(
$class
,
$validators
)
=
@$args
{
qw(class validators)
};
if
(
$class
->
isa
(
'Bugzilla::Product'
))
{
$validators
->
{
'votesperuser'
}
=
\&
_check_votesperuser
;
$validators
->
{
'maxvotesperbug'
}
=
\&
_check_maxvotesperbug
;
$validators
->
{
'votestoconfirm'
}
=
\&
_check_votestoconfirm
;
}
}
sub
object_before_create
{
my
(
$self
,
$args
)
=
@_
;
my
(
$class
,
$params
)
=
@$args
{
qw(class params)
};
if
(
$class
->
isa
(
'Bugzilla::Bug'
))
{
# Don't ever allow people to directly specify "votes" into the bugs
# table.
delete
$params
->
{
votes
};
}
elsif
(
$class
->
isa
(
'Bugzilla::Product'
))
{
my
$input
=
Bugzilla
->
input_params
;
$params
->
{
votesperuser
}
=
$input
->
{
'votesperuser'
};
$params
->
{
maxvotesperbug
}
=
$input
->
{
'maxvotesperbug'
};
$params
->
{
votestoconfirm
}
=
$input
->
{
'votestoconfirm'
};
}
}
sub
object_end_of_set_all
{
my
(
$self
,
$args
)
=
@_
;
my
(
$object
)
=
$args
->
{
object
};
if
(
$object
->
isa
(
'Bugzilla::Product'
))
{
my
$input
=
Bugzilla
->
input_params
;
$object
->
set
(
'votesperuser'
,
$input
->
{
'votesperuser'
});
$object
->
set
(
'maxvotesperbug'
,
$input
->
{
'maxvotesperbug'
});
$object
->
set
(
'votestoconfirm'
,
$input
->
{
'votestoconfirm'
});
}
}
sub
object_end_of_update
{
my
(
$self
,
$args
)
=
@_
;
my
(
$object
,
$changes
)
=
@$args
{
qw(object changes)
};
if
(
$object
->
isa
(
'Bugzilla::Product'
)
and
(
$changes
->
{
maxvotesperbug
}
or
$changes
->
{
votesperuser
}
or
$changes
->
{
votestoconfirm
})
)
{
_modify_bug_votes
(
$object
,
$changes
);
}
}
sub
bug_end_of_update
{
my
(
$self
,
$args
)
=
@_
;
my
(
$bug
,
$changes
)
=
@$args
{
qw(bug changes)
};
if
(
$changes
->
{
'product'
})
{
my
@msgs
;
# If some votes have been removed, RemoveVotes() returns
# a list of messages to send to voters.
@msgs
=
_remove_votes
(
$bug
->
id
,
0
,
'votes_bug_moved'
);
_confirm_if_vote_confirmed
(
$bug
->
id
);
foreach
my
$msg
(
@msgs
)
{
MessageToMTA
(
$msg
);
}
}
}
#############
# Templates #
#############
sub
template_before_create
{
my
(
$self
,
$args
)
=
@_
;
my
$config
=
$args
->
{
config
};
my
$constants
=
$config
->
{
CONSTANTS
};
$constants
->
{
REL_VOTER
}
=
REL_VOTER
;
$constants
->
{
CMT_POPULAR_VOTES
}
=
CMT_POPULAR_VOTES
;
$constants
->
{
DEFAULT_VOTES_PER_BUG
}
=
DEFAULT_VOTES_PER_BUG
;
}
sub
template_before_process
{
my
(
$self
,
$args
)
=
@_
;
my
(
$vars
,
$file
)
=
@$args
{
qw(vars file)
};
if
(
$file
eq
'admin/users/confirm-delete.html.tmpl'
)
{
my
$who
=
$vars
->
{
otheruser
};
my
$votes
=
Bugzilla
->
dbh
->
selectrow_array
(
'SELECT COUNT(*) FROM votes WHERE who = ?'
,
undef
,
$who
->
id
);
if
(
$votes
)
{
$vars
->
{
other_safe
}
=
1
;
$vars
->
{
votes
}
=
$votes
;
}
}
}
###########
# Bugmail #
###########
sub
bugmail_recipients
{
my
(
$self
,
$args
)
=
@_
;
my
(
$bug
,
$recipients
)
=
@$args
{
qw(bug recipients)
};
my
$dbh
=
Bugzilla
->
dbh
;
my
$voters
=
$dbh
->
selectcol_arrayref
(
"SELECT who FROM votes WHERE bug_id = ?"
,
undef
,
$bug
->
id
);
$recipients
->
{
$_
}
->
{
+
REL_VOTER
}
=
1
foreach
(
@$voters
);
}
sub
bugmail_relationships
{
my
(
$self
,
$args
)
=
@_
;
my
$relationships
=
$args
->
{
relationships
};
$relationships
->
{
+
REL_VOTER
}
=
'Voter'
;
}
###############
# Sanitycheck #
###############
sub
sanitycheck_check
{
my
(
$self
,
$args
)
=
@_
;
my
$status
=
$args
->
{
status
};
# Vote Cache
$status
->
(
'voting_count_start'
);
my
$dbh
=
Bugzilla
->
dbh
;
my
%
cached_counts
=
@
{
$dbh
->
selectcol_arrayref
(
'SELECT bug_id, votes FROM bugs'
,
{
Columns
=>
[
1
,
2
]})
};
my
%
real_counts
=
@
{
$dbh
->
selectcol_arrayref
(
'SELECT bug_id, SUM(vote_count) FROM votes '
.
$dbh
->
sql_group_by
(
'bug_id'
),
{
Columns
=>
[
1
,
2
]})
};
my
$needs_rebuild
;
foreach
my
$id
(
keys
%
cached_counts
)
{
my
$cached_count
=
$cached_counts
{
$id
};
my
$real_count
=
$real_counts
{
$id
}
||
0
;
if
(
$cached_count
<
0
)
{
$status
->
(
'voting_count_alert'
,
{
id
=>
$id
},
'alert'
);
}
elsif
(
$cached_count
!=
$real_count
)
{
$status
->
(
'voting_cache_alert'
,
{
id
=>
$id
},
'alert'
);
$needs_rebuild
=
1
;
}
}
$status
->
(
'voting_cache_rebuild_fix'
)
if
$needs_rebuild
;
}
sub
sanitycheck_repair
{
my
(
$self
,
$args
)
=
@_
;
my
$status
=
$args
->
{
status
};
my
$input
=
Bugzilla
->
input_params
;
my
$dbh
=
Bugzilla
->
dbh
;
return
if
!
$input
->
{
rebuild_vote_cache
};
$status
->
(
'voting_cache_rebuild_start'
);
$dbh
->
bz_start_transaction
();
$dbh
->
do
(
'UPDATE bugs SET votes = 0'
);
my
$sth
=
$dbh
->
prepare
(
'SELECT bug_id, SUM(vote_count) FROM votes '
.
$dbh
->
sql_group_by
(
'bug_id'
));
$sth
->
execute
();
my
$sth_update
=
$dbh
->
prepare
(
'UPDATE bugs SET votes = ? WHERE bug_id = ?'
);
while
(
my
(
$id
,
$count
)
=
$sth
->
fetchrow_array
)
{
$sth_update
->
execute
(
$count
,
$id
);
}
$dbh
->
bz_commit_transaction
();
$status
->
(
'voting_cache_rebuild_end'
);
}
##############
# Validators #
##############
sub
_check_votesperuser
{
return
_check_votes
(
0
,
@_
);
}
sub
_check_maxvotesperbug
{
return
_check_votes
(
DEFAULT_VOTES_PER_BUG
,
@_
);
}
sub
_check_votestoconfirm
{
return
_check_votes
(
0
,
@_
);
}
# This subroutine is only used internally by other _check_votes_* validators.
sub
_check_votes
{
my
(
$default
,
$invocant
,
$votes
,
$field
)
=
@_
;
detaint_natural
(
$votes
);
# On product creation, if the number of votes is not a valid integer,
# we silently fall back to the given default value.
# If the product already exists and the change is illegal, we complain.
if
(
!
defined
$votes
)
{
if
(
ref
$invocant
)
{
ThrowUserError
(
'voting_product_illegal_votes'
,
{
field
=>
$field
,
votes
=>
$_
[
2
]
});
}
else
{
$votes
=
$default
;
}
}
return
$votes
;
}
#########
# Pages #
#########
sub
page_before_template
{
my
(
$self
,
$args
)
=
@_
;
my
$page
=
$args
->
{
page_id
};
my
$vars
=
$args
->
{
vars
};
if
(
$page
=~
m{^voting/bug\.}
)
{
_page_bug
(
$vars
);
}
elsif
(
$page
=~
m{^voting/user\.}
)
{
_page_user
(
$vars
);
}
}
sub
_page_bug
{
my
(
$vars
)
=
@_
;
my
$dbh
=
Bugzilla
->
dbh
;
my
$template
=
Bugzilla
->
template
;
my
$input
=
Bugzilla
->
input_params
;
my
$bug_id
=
$input
->
{
bug_id
};
my
$bug
=
Bugzilla::
Bug
->
check
(
$bug_id
);
$vars
->
{
'bug'
}
=
$bug
;
$vars
->
{
'users'
}
=
$dbh
->
selectall_arrayref
(
'SELECT profiles.login_name,
profiles.userid AS id,
votes.vote_count
FROM votes
INNER JOIN profiles
ON profiles.userid = votes.who
WHERE votes.bug_id = ?'
,
{
Slice
=>
{}},
$bug
->
id
);
}
sub
_page_user
{
my
(
$vars
)
=
@_
;
my
$dbh
=
Bugzilla
->
dbh
;
my
$user
=
Bugzilla
->
user
;
my
$template
=
Bugzilla
->
template
;
my
$input
=
Bugzilla
->
input_params
;
my
$action
=
$input
->
{
action
};
if
(
$action
and
$action
eq
'vote'
)
{
_update_votes
(
$vars
);
}
# If a bug_id is given, and we're editing, we'll add it to the votes list.
my
$bug_id
=
$input
->
{
bug_id
};
my
$bug
=
Bugzilla::
Bug
->
check
(
$bug_id
)
if
$bug_id
;
my
$who_id
=
$input
->
{
user_id
}
||
$user
->
id
;
# Logged-out users must specify a user_id.
Bugzilla
->
login
(
LOGIN_REQUIRED
)
if
!
$who_id
;
my
$who
=
Bugzilla::
User
->
check
({
id
=>
$who_id
});
my
$canedit
=
$user
->
id
==
$who
->
id
;
$dbh
->
bz_start_transaction
();
if
(
$canedit
&&
$bug
)
{
# Make sure there is an entry for this bug
# in the vote table, just so that things display right.
my
$has_votes
=
$dbh
->
selectrow_array
(
'SELECT vote_count FROM votes
WHERE bug_id = ? AND who = ?'
,
undef
,
(
$bug
->
id
,
$who
->
id
));
if
(
!
$has_votes
)
{
$dbh
->
do
(
'INSERT INTO votes (who, bug_id, vote_count)
VALUES (?, ?, 0)'
,
undef
,
(
$who
->
id
,
$bug
->
id
));
}
}
my
(
@products
,
@all_bug_ids
);
# Read the votes data for this user for each product.
foreach
my
$product
(
@
{
$user
->
get_selectable_products
})
{
next
unless
(
$product
->
{
votesperuser
}
>
0
);
my
@bugs
;
my
@bug_ids
;
my
$total
=
0
;
my
$onevoteonly
=
0
;
my
$vote_list
=
$dbh
->
selectall_arrayref
(
'SELECT votes.bug_id, votes.vote_count,
bugs.short_desc
FROM votes
INNER JOIN bugs
ON votes.bug_id = bugs.bug_id
WHERE votes.who = ?
AND bugs.product_id = ?
ORDER BY votes.bug_id'
,
undef
,
(
$who
->
id
,
$product
->
id
));
foreach
(
@$vote_list
)
{
my
(
$id
,
$count
,
$summary
)
=
@$_
;
$total
+=
$count
;
# Next if user can't see this bug. So, the totals will be correct
# and they can see there are votes 'missing', but not on what bug
# they are. This seems a reasonable compromise; the alternative is
# to lie in the totals.
next
if
!
$user
->
can_see_bug
(
$id
);
push
(
@bugs
,
{
id
=>
$id
,
summary
=>
$summary
,
count
=>
$count
});
push
(
@bug_ids
,
$id
);
push
(
@all_bug_ids
,
$id
);
}
$onevoteonly
=
1
if
(
min
(
$product
->
{
votesperuser
},
$product
->
{
maxvotesperbug
})
==
1
);
# Only add the product for display if there are any bugs in it.
if
(
$#bugs
>
-
1
)
{
push
(
@products
,
{
name
=>
$product
->
name
,
bugs
=>
\
@bugs
,
bug_ids
=>
\
@bug_ids
,
onevoteonly
=>
$onevoteonly
,
total
=>
$total
,
maxvotes
=>
$product
->
{
votesperuser
},
maxperbug
=>
$product
->
{
maxvotesperbug
}
});
}
}
$dbh
->
do
(
'DELETE FROM votes WHERE vote_count <= 0'
);
$dbh
->
bz_commit_transaction
();
$vars
->
{
'canedit'
}
=
$canedit
;
$vars
->
{
'voting_user'
}
=
{
"login"
=>
$who
->
name
};
$vars
->
{
'products'
}
=
\
@products
;
$vars
->
{
'this_bug'
}
=
$bug
;
$vars
->
{
'all_bug_ids'
}
=
\
@all_bug_ids
;
}
sub
_update_votes
{
my
(
$vars
)
=
@_
;
############################################################################
# Begin Data/Security Validation
############################################################################
my
$cgi
=
Bugzilla
->
cgi
;
my
$dbh
=
Bugzilla
->
dbh
;
my
$template
=
Bugzilla
->
template
;
my
$user
=
Bugzilla
->
login
(
LOGIN_REQUIRED
);
my
$input
=
Bugzilla
->
input_params
;
# Build a list of bug IDs for which votes have been submitted. Votes
# are submitted in form fields in which the field names are the bug
# IDs and the field values are the number of votes.
my
@buglist
=
grep
{
/^\d+$/
}
keys
%
$input
;
# If no bugs are in the buglist, let's make sure the user gets notified
# that their votes will get nuked if they continue.
if
(
scalar
(
@buglist
)
==
0
)
{
if
(
!
defined
$cgi
->
param
(
'delete_all_votes'
))
{
print
$cgi
->
header
();
$template
->
process
(
"voting/delete-all.html.tmpl"
,
$vars
)
||
ThrowTemplateError
(
$template
->
error
());
exit
;
}
elsif
(
$cgi
->
param
(
'delete_all_votes'
)
==
0
)
{
print
$cgi
->
redirect
(
"page.cgi?id=voting/user.html"
);
exit
;
}
}
# Call check() on each bug ID to make sure it is a positive
# integer representing an existing bug that the user is authorized
# to access, and make sure the number of votes submitted is also
# a non-negative integer (a series of digits not preceded by a
# minus sign).
my
(
%
votes
,
@bugs
);
foreach
my
$id
(
@buglist
)
{
my
$bug
=
Bugzilla::
Bug
->
check
(
$id
);
push
(
@bugs
,
$bug
);
$id
=
$bug
->
id
;
$votes
{
$id
}
=
$input
->
{
$id
};
detaint_natural
(
$votes
{
$id
})
||
ThrowUserError
(
"voting_must_be_nonnegative"
);
}
############################################################################
# End Data/Security Validation
############################################################################
my
$who
=
$user
->
id
;
# If the user is voting for bugs, make sure they aren't overstuffing
# the ballot box.
if
(
scalar
@bugs
)
{
my
(
%
prodcount
,
%
products
);
foreach
my
$bug
(
@bugs
)
{
my
$bug_id
=
$bug
->
id
;
my
$prod
=
$bug
->
product
;
$products
{
$prod
}
||=
$bug
->
product_obj
;
$prodcount
{
$prod
}
||=
0
;
$prodcount
{
$prod
}
+=
$votes
{
$bug_id
};
# Make sure we haven't broken the votes-per-bug limit
(
$votes
{
$bug_id
}
<=
$products
{
$prod
}
->
{
maxvotesperbug
})
||
ThrowUserError
(
"voting_too_many_votes_for_bug"
,
{
max
=>
$products
{
$prod
}
->
{
maxvotesperbug
},
product
=>
$prod
,
votes
=>
$votes
{
$bug_id
}});
}
# Make sure we haven't broken the votes-per-product limit
foreach
my
$prod
(
keys
(
%
prodcount
))
{
(
$prodcount
{
$prod
}
<=
$products
{
$prod
}
->
{
votesperuser
})
||
ThrowUserError
(
"voting_too_many_votes_for_product"
,
{
max
=>
$products
{
$prod
}
->
{
votesperuser
},
product
=>
$prod
,
votes
=>
$prodcount
{
$prod
}});
}
}
# Update the user's votes in the database. If the user did not submit
# any votes, they may be using a form with checkboxes to remove all their
# votes (checkboxes are not submitted along with other form data when
# they are not checked, and Bugzilla uses them to represent single votes
# for products that only allow one vote per bug). In that case, we still
# need to clear the user's votes from the database.
my
%
affected
;
$dbh
->
bz_start_transaction
();
# Take note of, and delete the user's old votes from the database.
my
$bug_list
=
$dbh
->
selectcol_arrayref
(
'SELECT bug_id FROM votes
WHERE who = ?'
,
undef
,
$who
);
foreach
my
$id
(
@$bug_list
)
{
$affected
{
$id
}
=
1
;
}
$dbh
->
do
(
'DELETE FROM votes WHERE who = ?'
,
undef
,
$who
);
my
$sth_insertVotes
=
$dbh
->
prepare
(
'INSERT INTO votes (who, bug_id, vote_count)
VALUES (?, ?, ?)'
);
# Insert the new values in their place
foreach
my
$id
(
@buglist
)
{
if
(
$votes
{
$id
}
>
0
)
{
$sth_insertVotes
->
execute
(
$who
,
$id
,
$votes
{
$id
});
}
$affected
{
$id
}
=
1
;
}
# Update the cached values in the bugs table
print
$cgi
->
header
();
my
@updated_bugs
=
();
my
$sth_getVotes
=
$dbh
->
prepare
(
"SELECT SUM(vote_count) FROM votes
WHERE bug_id = ?"
);
my
$sth_updateVotes
=
$dbh
->
prepare
(
"UPDATE bugs SET votes = ?
WHERE bug_id = ?"
);
foreach
my
$id
(
keys
%
affected
)
{
$sth_getVotes
->
execute
(
$id
);
my
$v
=
$sth_getVotes
->
fetchrow_array
||
0
;
$sth_updateVotes
->
execute
(
$v
,
$id
);
my
$confirmed
=
_confirm_if_vote_confirmed
(
$id
);
push
(
@updated_bugs
,
$id
)
if
$confirmed
;
}
$dbh
->
bz_commit_transaction
();
$vars
->
{
'type'
}
=
"votes"
;
$vars
->
{
'mailrecipients'
}
=
{
'changer'
=>
$user
->
login
};
$vars
->
{
'title_tag'
}
=
'change_votes'
;
foreach
my
$bug_id
(
@updated_bugs
)
{
$vars
->
{
'id'
}
=
$bug_id
;
$template
->
process
(
"bug/process/results.html.tmpl"
,
$vars
)
||
ThrowTemplateError
(
$template
->
error
());
# Set header_done to 1 only after the first bug.
$vars
->
{
'header_done'
}
=
1
;
}
$vars
->
{
'votes_recorded'
}
=
1
;
}
######################
# Helper Subroutines #
######################
sub
_modify_bug_votes
{
my
(
$product
,
$changes
)
=
@_
;
my
$dbh
=
Bugzilla
->
dbh
;
my
@msgs
;
# 1. too many votes for a single user on a single bug.
my
@toomanyvotes_list
;
if
(
$product
->
{
maxvotesperbug
}
<
$product
->
{
votesperuser
})
{
my
$votes
=
$dbh
->
selectall_arrayref
(
'SELECT votes.who, votes.bug_id
FROM votes
INNER JOIN bugs ON bugs.bug_id = votes.bug_id
WHERE bugs.product_id = ?
AND votes.vote_count > ?'
,
undef
,
(
$product
->
id
,
$product
->
{
maxvotesperbug
}));
foreach
my
$vote
(
@$votes
)
{
my
(
$who
,
$id
)
=
(
@$vote
);
# If some votes are removed, _remove_votes() returns a list
# of messages to send to voters.
push
(
@msgs
,
_remove_votes
(
$id
,
$who
,
'votes_too_many_per_bug'
));
my
$name
=
user_id_to_login
(
$who
);
push
(
@toomanyvotes_list
,
{
id
=>
$id
,
name
=>
$name
});
}
}
$changes
->
{
'too_many_votes'
}
=
\
@toomanyvotes_list
;
# 2. too many total votes for a single user.
# This part doesn't work in the general case because _remove_votes
# doesn't enforce votesperuser (except per-bug when it's less
# than maxvotesperbug). See _remove_votes().
my
$votes
=
$dbh
->
selectall_arrayref
(
'SELECT votes.who, votes.vote_count
FROM votes
INNER JOIN bugs ON bugs.bug_id = votes.bug_id
WHERE bugs.product_id = ?'
,
undef
,
$product
->
id
);
my
%
counts
;
foreach
my
$vote
(
@$votes
)
{
my
(
$who
,
$count
)
=
@$vote
;
if
(
!
defined
$counts
{
$who
})
{
$counts
{
$who
}
=
$count
;
}
else
{
$counts
{
$who
}
+=
$count
;
}
}
my
@toomanytotalvotes_list
;
foreach
my
$who
(
keys
(
%
counts
))
{
if
(
$counts
{
$who
}
>
$product
->
{
votesperuser
})
{
my
$bug_ids
=
$dbh
->
selectcol_arrayref
(
'SELECT votes.bug_id
FROM votes
INNER JOIN bugs ON bugs.bug_id = votes.bug_id
WHERE bugs.product_id = ?
AND votes.who = ?'
,
undef
,
$product
->
id
,
$who
);
foreach
my
$bug_id
(
@$bug_ids
)
{
# _remove_votes returns a list of messages to send
# in case some voters had too many votes.
push
(
@msgs
,
_remove_votes
(
$bug_id
,
$who
,
'votes_too_many_per_user'
));
my
$name
=
user_id_to_login
(
$who
);
push
(
@toomanytotalvotes_list
,
{
id
=>
$bug_id
,
name
=>
$name
});
}
}
}
$changes
->
{
'too_many_total_votes'
}
=
\
@toomanytotalvotes_list
;
# 3. enough votes to confirm
my
$bug_list
=
$dbh
->
selectcol_arrayref
(
'SELECT bug_id FROM bugs
WHERE product_id = ? AND bug_status = ? AND votes >= ?'
,
undef
,
(
$product
->
id
,
'UNCONFIRMED'
,
$product
->
{
votestoconfirm
}));
my
@updated_bugs
;
foreach
my
$bug_id
(
@$bug_list
)
{
my
$confirmed
=
_confirm_if_vote_confirmed
(
$bug_id
);
push
(
@updated_bugs
,
$bug_id
)
if
$confirmed
;
}
$changes
->
{
'confirmed_bugs'
}
=
\
@updated_bugs
;
# Now that changes are done, we can send emails to voters.
foreach
my
$msg
(
@msgs
)
{
MessageToMTA
(
$msg
);
}
}
# If a bug is moved to a product which allows less votes per bug
# compared to the previous product, extra votes need to be removed.
sub
_remove_votes
{
my
(
$id
,
$who
,
$reason
)
=
(
@_
);
my
$dbh
=
Bugzilla
->
dbh
;
my
$whopart
=
(
$who
)
?
" AND votes.who = $who"
:
""
;
my
$sth
=
$dbh
->
prepare
(
"SELECT profiles.login_name, "
.
"profiles.userid, votes.vote_count, "
.
"products.votesperuser, products.maxvotesperbug "
.
"FROM profiles "
.
"LEFT JOIN votes ON profiles.userid = votes.who "
.
"LEFT JOIN bugs ON votes.bug_id = bugs.bug_id "
.
"LEFT JOIN products ON products.id = bugs.product_id "
.
"WHERE votes.bug_id = ? "
.
$whopart
);
$sth
->
execute
(
$id
);
my
@list
;
while
(
my
(
$name
,
$userid
,
$oldvotes
,
$votesperuser
,
$maxvotesperbug
)
=
$sth
->
fetchrow_array
())
{
push
(
@list
,
[
$name
,
$userid
,
$oldvotes
,
$votesperuser
,
$maxvotesperbug
]);
}
# @messages stores all emails which have to be sent, if any.
# This array is passed to the caller which will send these emails itself.
my
@messages
=
();
if
(
scalar
(
@list
))
{
foreach
my
$ref
(
@list
)
{
my
(
$name
,
$userid
,
$oldvotes
,
$votesperuser
,
$maxvotesperbug
)
=
(
@$ref
);
$maxvotesperbug
=
min
(
$votesperuser
,
$maxvotesperbug
);
# If this product allows voting and the user's votes are in
# the acceptable range, then don't do anything.
next
if
$votesperuser
&&
$oldvotes
<=
$maxvotesperbug
;
# If the user has more votes on this bug than this product
# allows, then reduce the number of votes so it fits
my
$newvotes
=
$maxvotesperbug
;
my
$removedvotes
=
$oldvotes
-
$newvotes
;
if
(
$newvotes
)
{
$dbh
->
do
(
"UPDATE votes SET vote_count = ? "
.
"WHERE bug_id = ? AND who = ?"
,
undef
,
(
$newvotes
,
$id
,
$userid
));
}
else
{
$dbh
->
do
(
"DELETE FROM votes WHERE bug_id = ? AND who = ?"
,
undef
,
(
$id
,
$userid
));
}
# Notice that we did not make sure that the user fit within the $votesperuser
# range. This is considered to be an acceptable alternative to losing votes
# during product moves. Then next time the user attempts to change their votes,
# they will be forced to fit within the $votesperuser limit.
# Now lets send the e-mail to alert the user to the fact that their votes have
# been reduced or removed.
my
$vars
=
{
'to'
=>
$name
.
Bugzilla
->
params
->
{
'emailsuffix'
},
'bugid'
=>
$id
,
'reason'
=>
$reason
,
'votesremoved'
=>
$removedvotes
,
'votesold'
=>
$oldvotes
,
'votesnew'
=>
$newvotes
,
};
my
$voter
=
new
Bugzilla::
User
(
$userid
);
my
$template
=
Bugzilla
->
template_inner
(
$voter
->
settings
->
{
'lang'
}
->
{
'value'
});
my
$msg
;
$template
->
process
(
"voting/votes-removed.txt.tmpl"
,
$vars
,
\
$msg
);
push
(
@messages
,
$msg
);
}
Bugzilla
->
template_inner
(
""
);
my
$votes
=
$dbh
->
selectrow_array
(
"SELECT SUM(vote_count) "
.
"FROM votes WHERE bug_id = ?"
,
undef
,
$id
)
||
0
;
$dbh
->
do
(
"UPDATE bugs SET votes = ? WHERE bug_id = ?"
,
undef
,
(
$votes
,
$id
));
}
# Now return the array containing emails to be sent.
return
@messages
;
}
# If a user votes for a bug, or the number of votes required to
# confirm a bug has been reduced, check if the bug is now confirmed.
sub
_confirm_if_vote_confirmed
{
my
$id
=
shift
;
my
$bug
=
new
Bugzilla::
Bug
(
$id
);
my
$ret
=
0
;
if
(
!
$bug
->
everconfirmed
and
$bug
->
product_obj
->
{
votestoconfirm
}
and
$bug
->
votes
>=
$bug
->
product_obj
->
{
votestoconfirm
})
{
$bug
->
add_comment
(
''
,
{
type
=>
CMT_POPULAR_VOTES
});
if
(
$bug
->
bug_status
eq
'UNCONFIRMED'
)
{
# Get a valid open state.
my
$new_status
;
foreach
my
$state
(
@
{
$bug
->
status
->
can_change_to
})
{
if
(
$state
->
is_open
&&
$state
->
name
ne
'UNCONFIRMED'
)
{
$new_status
=
$state
->
name
;
last
;
}
}
ThrowCodeError
(
'no_open_bug_status'
)
unless
$new_status
;
# We cannot call $bug->set_status() here, because a user without
# canconfirm privs should still be able to confirm a bug by
# popular vote. We already know the new status is valid, so it's safe.
$bug
->
{
bug_status
}
=
$new_status
;
$bug
->
{
everconfirmed
}
=
1
;
delete
$bug
->
{
'status'
};
# Contains the status object.
}
else
{
# If the bug is in a closed state, only set everconfirmed to 1.
# Do not call $bug->_set_everconfirmed(), for the same reason as above.
$bug
->
{
everconfirmed
}
=
1
;
}
$bug
->
update
();
$ret
=
1
;
}
return
$ret
;
}
__PACKAGE__
->
NAME
;
extensions/Voting/template/en/default/hook/account/prefs/email-relationships.html.tmpl
0 → 100644
View file @
120b63d5
[%# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# The Initial Developer of the Original Code is Everything Solved, Inc.
# Portions created by the Initial Developer are Copyright (C) 2010
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Max Kanat-Alexander <mkanat@bugzilla.org>
#%]
[% relationships.push({ id = constants.REL_VOTER, description = "Voter" }) %]
[% no_added_removed.push(constants.REL_VOTER) %]
extensions/Voting/template/en/default/hook/admin/products/edit-common-rows.html.tmpl
0 → 100644
View file @
120b63d5
[%# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# The Initial Developer of the Original Code is Everything Solved, Inc.
# Portions created by the Initial Developer are Copyright (C) 2010
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Max Kanat-Alexander <mkanat@bugzilla.org>
#%]
[% DEFAULT
product.maxvotesperbug = constants.DEFAULT_VOTES_PER_BUG
product.votesperuser = 0
product.votestoconfirm = 0
%]
<tr>
<th align="right">Maximum votes per person:</th>
<td><input size="5" maxlength="5" name="votesperuser" id="votesperuser"
value="[% product.votesperuser FILTER html %]">
</td>
</tr>
<tr>
<th align="right">
Maximum votes a person can put on a single [% terms.bug %]:
</th>
<td><input size="5" maxlength="5" name="maxvotesperbug" id="maxvotesperbug"
value="[% product.maxvotesperbug FILTER html %]">
</td>
</tr>
<tr id="votes_to_confirm_container"
[%- ' class="bz_default_hidden"' IF !product.allows_unconfirmed %]>
<th align="right">
Confirm [% terms.abug %] if it gets this many votes:
</th>
<td>
<input size="3" maxlength="5" name="votestoconfirm" id="votestoconfirm"
value="[% product.votestoconfirm FILTER html %]">
<br>(Setting this to 0 disables auto-confirming [% terms.bugs %]
by vote.)
<script type="text/javascript">
YAHOO.util.Event.addListener('allows_unconfirmed', 'change',
function() { bz_toggleClass('votes_to_confirm_container',
'bz_default_hidden'); });
</script>
</td>
</tr>
extensions/Voting/template/en/default/hook/admin/products/updated-changes.html.tmpl
0 → 100644
View file @
120b63d5
[%# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# The Initial Developer of the Original Code is Everything Solved, Inc.
# Portions created by the Initial Developer are Copyright (C) 2010
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Max Kanat-Alexander <mkanat@bugzilla.org>
#%]
[% SET checkvotes = 0 %]
[% IF changes.votesperuser.defined %]
<p>
Updated votes per user from
[%+ changes.votesperuser.0 FILTER html %] to
[%+ product.votesperuser FILTER html %].
</p>
[% checkvotes = 1 %]
[% END %]
[% IF changes.maxvotesperbug.defined %]
<p>
Updated maximum votes per [% terms.bug %] from
[%+ changes.maxvotesperbug.0 FILTER html %] to
[%+ product.maxvotesperbug FILTER html %].
</p>
[% checkvotes = 1 %]
[% END %]
[% IF changes.votestoconfirm.defined %]
<p>
Updated number of votes needed to confirm a [% terms.bug %] from
[%+ changes.votestoconfirm.0 FILTER html %] to
[%+ product.votestoconfirm FILTER html %].
</p>
[% checkvotes = 1 %]
[% END %]
[%# Note that this display of changed votes and/or confirmed bugs is
not very scalable. We could have a _lot_, and we just list them all.
One day we should limit this perhaps, or have a more scalable display %]
[% IF checkvotes %]
<hr>
<p>Checking existing votes in this product for anybody who now
has too many votes for [% terms.abug %]...<br>
[% IF changes.too_many_votes.size %]
[% FOREACH detail = changes.too_many_votes %]
→removed votes for [% terms.bug %] <a href="show_bug.cgi?id=
[%- detail.id FILTER url_quote %]">
[%- detail.id FILTER html %]</a> from [% detail.name FILTER html %]<br>
[% END %]
[% ELSE %]
→there were none.
[% END %]
</p>
<p>Checking existing votes in this product for anybody
who now has too many total votes...<br>
[% IF changes.too_many_total_votes.size %]
[% FOREACH detail = changes.too_many_total_votes %]
→removed votes for [% terms.bug %] <a href="show_bug.cgi?id=
[%- detail.id FILTER url_quote %]">
[%- detail.id FILTER html %]</a> from [% detail.name FILTER html %]<br>
[% END %]
[% ELSE %]
→there were none.
[% END %]
</p>
<p>Checking unconfirmed [% terms.bugs %] in this product for any which now have
sufficient votes...<br>
[% IF changes.confirmed_bugs.size %]
[% FOREACH id = changes.confirmed_bugs %]
[%# This is INCLUDED instead of PROCESSED to avoid variables getting
overwritten, which happens otherwise %]
[% INCLUDE bug/process/results.html.tmpl
type = 'votes'
mailrecipients = { 'changer' => user.login }
header_done = 1
id = id
%]
[% END %]
[% ELSE %]
→there were none.
[% END %]
</p>
[% END %]
extensions/Voting/template/en/default/hook/admin/sanitycheck/messages-statuses.html.tmpl
0 → 100644
View file @
120b63d5
[%# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# The Initial Developer of the Original Code is Everything Solved, Inc.
# Portions created by the Initial Developer are Copyright (C) 2010
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Max Kanat-Alexander <mkanat@bugzilla.org>
#%]
[% IF san_tag == "voting_cache_rebuild_fix" %]
<a href="sanitycheck.cgi?rebuild_vote_cache=1">Click here to
rebuild the vote cache</a>
[% ELSIF san_tag == "voting_cache_alert" %]
Bad vote cache for [% PROCESS bug_link bug_id = id %]
[% ELSIF san_tag == "voting_count_start" %]
Checking cached vote counts.
[% ELSIF san_tag == "voting_count_alert" %]
Bad vote sum for [% terms.bug %] [%+ id FILTER html %].
[% ELSIF san_tag == "voting_cache_rebuild_start" %]
OK, now rebuilding vote cache.
[% ELSIF san_tag == "voting_cache_rebuild_end" %]
Vote cache has been rebuilt
[% END %]
extensions/Voting/template/en/default/hook/admin/users/confirm-delete-warn_safe.html.tmpl
0 → 100644
View file @
120b63d5
[%# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# The Initial Developer of the Original Code is Everything Solved, Inc.
# Portions created by the Initial Developer are Copyright (C) 2010
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Max Kanat-Alexander <mkanat@bugzilla.org>
#%]
[% IF votes %]
<li>
[% otheruser.login FILTER html %] has voted on
[% IF votes == 1 %]
[%+ terms.abug %]
[% ELSE %]
[%+ votes %] [%+ terms.bugs %]
[% END %].
If you delete the user account,
[% IF votes == 1 %]
this vote
[% ELSE %]
these votes
[% END %]
will be deleted along with the user account.
</li>
[% END %]
extensions/Voting/template/en/default/hook/bug/edit-after_importance.html.tmpl
0 → 100644
View file @
120b63d5
[%# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# The Initial Developer of the Original Code is Everything Solved, Inc.
# Portions created by the Initial Developer are Copyright (C) 2010
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Max Kanat-Alexander <mkanat@bugzilla.org>
#%]
[% IF bug.product_obj.votesperuser %]
<style type="text/css">
#votes_container { white-space: nowrap; }
</style>
<span id="votes_container">
[% IF bug.votes %]
with
<a href="page.cgi?id=voting/bug.html?bug_id=
[%- bug.id FILTER url_quote %]">
[%- bug.votes %]
[% IF bug.votes == 1 %]
vote
[% ELSE %]
votes
[% END %]</a>
[% END %]
(<a href="page.cgi?id=voting/user.html&bug_id=
[%- bug.id FILTER url_quote %]#vote_
[%- bug.id FILTER url_quote %]">vote</a>)
</span>
[% END %]
extensions/Voting/template/en/default/hook/bug/format_comment-type.txt.tmpl
0 → 100644
View file @
120b63d5
[%# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# The Initial Developer of the Original Code is Everything Solved, Inc.
# Portions created by the Initial Developer are Copyright (C) 2010
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Max Kanat-Alexander <mkanat@bugzilla.org>
#%]
[% IF comment.type == constants.CMT_POPULAR_VOTES %]
*** This [% terms.bug %] has been confirmed by popular vote. ***
[% END %]
extensions/Voting/template/en/default/hook/bug/process/header-title.html.tmpl
0 → 100644
View file @
120b63d5
[%# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# The Initial Developer of the Original Code is Everything Solved, Inc.
# Portions created by the Initial Developer are Copyright (C) 2010
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Max Kanat-Alexander <mkanat@bugzilla.org>
#%]
[% IF title_tag == "change_votes" %]
[% title = "Change Votes" %]
[% END %]
extensions/Voting/template/en/default/hook/bug/process/results-title.html.tmpl
0 → 100644
View file @
120b63d5
[%# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# The Initial Developer of the Original Code is Everything Solved, Inc.
# Portions created by the Initial Developer are Copyright (C) 2010
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Max Kanat-Alexander <mkanat@bugzilla.org>
#%]
[% title.votes = "$Link confirmed by number of votes" %]
extensions/Voting/template/en/default/hook/global/field-descs-end.none.tmpl
0 → 100644
View file @
120b63d5
[%# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# The Initial Developer of the Original Code is Everything Solved, Inc.
# Portions created by the Initial Developer are Copyright (C) 2010
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Max Kanat-Alexander <mkanat@bugzilla.org>
#%]
[% field_descs.votes = "Votes" %]
extensions/Voting/template/en/default/hook/global/reason-descs-end.none.tmpl
0 → 100644
View file @
120b63d5
[%# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# The Initial Developer of the Original Code is Everything Solved, Inc.
# Portions created by the Initial Developer are Copyright (C) 2010
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Max Kanat-Alexander <mkanat@bugzilla.org>
#%]
[% reason_descs.${constants.REL_VOTER} = "You voted for the ${terms.bug}." %]
[% watch_reason_descs.${constants.REL_VOTER} =
"You are watching a voter for the ${terms.bug}." %]
extensions/Voting/template/en/default/hook/global/user-error-errors.html.tmpl
0 → 100644
View file @
120b63d5
[%# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# The Initial Developer of the Original Code is Everything Solved, Inc.
# Portions created by the Initial Developer are Copyright (C) 2010
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Max Kanat-Alexander <mkanat@bugzilla.org>
#%]
[% IF error == "voting_must_be_nonnegative" %]
[% title = "Votes Must Be Non-negative" %]
[% admindocslinks = {'voting.html' => 'Setting up the voting feature'} %]
Only use non-negative numbers for your [% terms.bug %] votes.
[% ELSIF error == "voting_product_illegal_votes" %]
[% title = "Votes Must Be Non-negative" %]
[% admindocslinks = {'voting.html' => 'Setting up the voting feature'} %]
'[% votes FILTER html %]' is an invalid value for the
<em>
[% IF field == "votesperuser" %]
Votes Per User
[% ELSIF field == "maxvotesperbug" %]
Maximum Votes Per [% terms.Bug %]
[% ELSIF field == "votestoconfirm" %]
Votes To Confirm
[% END %]
</em> field, which should contain a non-negative number.
[% ELSIF error == "voting_too_many_votes_for_bug" %]
[% title = "Illegal Vote" %]
[% admindocslinks = {'voting.html' => 'Setting up the voting feature'} %]
You may only use at most [% max FILTER html %] votes for a single
[%+ terms.bug %] in the
<tt>[% product FILTER html %]</tt> product, but you are trying to
use [% votes FILTER html %].
[% ELSIF error == "voting_too_many_votes_for_product" %]
[% title = "Illegal Vote" %]
[% admindocslinks = {'voting.html' => 'Setting up the voting feature'} %]
You tried to use [% votes FILTER html %] votes in the
<tt>[% product FILTER html %]</tt> product, which exceeds the maximum of
[%+ max FILTER html %] votes for this product.
[% END %]
extensions/Voting/template/en/default/hook/search/form-email_numbering_end.html.tmpl
0 → 100644
View file @
120b63d5
[%# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# The Initial Developer of the Original Code is Everything Solved, Inc.
# Portions created by the Initial Developer are Copyright (C) 2010
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Max Kanat-Alexander <mkanat@bugzilla.org>
#%]
<tr>
<td align="right">
<label for="votes">Only [% terms.bugs %] with at least</label>:
</td>
<td>
<input name="votes" id="votes" size="3"
value="[% default.votes.0 FILTER html %]"> votes
<input type="hidden" name="votes_type" value="greaterthaneq">
</td>
</tr>
extensions/Voting/template/en/default/hook/search/search-report-select-rep_fields.html.tmpl
0 → 100644
View file @
120b63d5
[%# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# The Initial Developer of the Original Code is Everything Solved, Inc.
# Portions created by the Initial Developer are Copyright (C) 2010
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Max Kanat-Alexander <mkanat@bugzilla.org>
#%]
[% rep_fields.push('votes') %]
template/en/default/pages/voting.html.tmpl
→
extensions/Voting/
template/en/default/pages/voting.html.tmpl
View file @
120b63d5
...
...
@@ -64,6 +64,6 @@ a few [% terms.bugs %] indicating your strong support for them.
on [% terms.bugs %] you vote for.</p>
<p>You may review your votes at any time by clicking on the "<a href=
"
votes.cgi?action=show_user
">My Votes</a>" link in the page footer.</p>
"
page.cgi?id=voting/user.html
">My Votes</a>" link in the page footer.</p>
[% INCLUDE global/footer.html.tmpl %]
template/en/default/bug/votes/list-for-
bug.html.tmpl
→
extensions/Voting/template/en/default/pages/voting/
bug.html.tmpl
View file @
120b63d5
...
...
@@ -16,10 +16,11 @@
# Rights Reserved.
#
# Contributor(s): Gervase Markham <gerv@gerv.net>
# Max Kanat-Alexander <mkanat@bugzilla.org>
#%]
[%# INTERFACE:
# bug
_id: integer. ID of the bug
we are listing the votes for.
# bug
: Bugzilla::Bug that
we are listing the votes for.
# users: list of hashes. May be empty. Each hash has two members:
# login_name: string. The login name of the user whose vote is attached
# vote_count: integer. The number of times that user has votes for this bug.
...
...
@@ -29,7 +30,7 @@
[% PROCESS global/header.html.tmpl
title = "Show Votes"
subheader = "$terms.Bug
<a href=\"show_bug.cgi?id=$bug_id\">$bug_id</a>"
subheader = "$terms.Bug
$bug.id" FILTER bug_link(bug)
%]
[% total = 0 %]
...
...
@@ -43,18 +44,18 @@
[% total = total + voter.vote_count %]
<tr>
<td>
<a href="
votes.cgi?action=show_user
&user_id=
<a href="
page.cgi?id=voting/user.html
&user_id=
[%- voter.id FILTER url_quote %]">
[% voter.login_name FILTER email FILTER html %]
</a>
</td>
<td align="right">
[% voter.vote_count %]
[% voter.vote_count
FILTER html
%]
</td>
</tr>
[% END %]
</table>
<p>Total votes: [% total %]</p>
<p>Total votes: [% total
FILTER html
%]</p>
[% PROCESS global/footer.html.tmpl %]
template/en/default/bug/votes/list-for-
user.html.tmpl
→
extensions/Voting/template/en/default/pages/voting/
user.html.tmpl
View file @
120b63d5
...
...
@@ -31,7 +31,7 @@
# maxvotes: max votes allowed for a user in this product
# maxperbug: max votes per bug allowed for a user in this product
#
#
bug_id: number; if the user is voting for a bug, this is the bug id
#
this_bug: Bugzilla::Bug; if the user is voting for a bug, this is the bug
#
# canedit: boolean; Should the votes be presented in a form, or readonly?
#
...
...
@@ -44,18 +44,18 @@
[% subheader = voting_user.login FILTER html %]
[% IF canedit %]
[% title = "Change Votes" %]
[% IF
bug_id
%]
[% IF
this_bug
%]
[%# We .select and .focus the input so it works for textbox and
checkbox %]
[% onload = "document.forms['voting_form'].bug_" _
bug_
id _
".select();document.forms['voting_form'].bug_" _
bug_
id _
[% onload = "document.forms['voting_form'].bug_" _
this_bug.
id _
".select();document.forms['voting_form'].bug_" _
this_bug.
id _
".focus()" %]
[% END %]
[% ELSE %]
[% title = "Show Votes" %]
[% END %]
[% PROCESS global/header.html.tmpl
style_urls = [ "
skins/standard/voting
.css" ]
style_urls = [ "
extensions/Voting/web/style
.css" ]
%]
[% ELSE %]
<hr>
...
...
@@ -72,7 +72,7 @@
[% END %]
[% IF products.size %]
<form name="voting_form" method="post" action="
votes.cgi
">
<form name="voting_form" method="post" action="
page.cgi?id=voting/user.html
">
<input type="hidden" name="action" value="vote">
<table cellspacing="4">
<tr>
...
...
@@ -108,9 +108,9 @@
</tr>
[% FOREACH bug = product.bugs %]
<tr [% IF bug.id ==
bug_
id && canedit %]
<tr [% IF bug.id ==
this_bug.
id && canedit %]
class="bz_bug_being_voted_on" [% END %]>
<td>[% IF bug.id ==
bug_
id && canedit %]Enter New Vote here →
<td>[% IF bug.id ==
this_bug.
id && canedit %]Enter New Vote here →
[%- END %]</td>
<td align="right"><a name="vote_[% bug.id %]">
[% IF canedit %]
...
...
@@ -130,7 +130,7 @@
</td>
<td>
[% bug.summary FILTER html %]
(<a href="
votes.cgi?action=show_bug
&bug_id=[% bug.id %]">Show Votes</a>)
(<a href="
page.cgi?id=voting/bug.html
&bug_id=[% bug.id %]">Show Votes</a>)
</td>
</tr>
[% END %]
...
...
template/en/default/bug/votes
/delete-all.html.tmpl
→
extensions/Voting/template/en/default/voting
/delete-all.html.tmpl
View file @
120b63d5
...
...
@@ -33,7 +33,7 @@
remove your vote from every [% terms.bug %] you've voted on?
</p>
<form action="
votes.cgi
" method="post">
<form action="
page.cgi?id=voting/user.html
" method="post">
<input type="hidden" name="action" value="vote">
<p>
<input type="radio" name="delete_all_votes" value="1">
...
...
template/en/default/email
/votes-removed.txt.tmpl
→
extensions/Voting/template/en/default/voting
/votes-removed.txt.tmpl
View file @
120b63d5
File moved
skins/standard/voting
.css
→
extensions/Voting/web/style
.css
View file @
120b63d5
File moved
importxml.pl
View file @
120b63d5
...
...
@@ -980,7 +980,6 @@ sub process_bug {
if
(
$status
eq
"UNCONFIRMED"
){
$err
.=
"Bug Status was UNCONFIRMED but everconfirmed was true\n"
;
$err
.=
" Setting status to $initial_status\n"
;
$err
.=
"Resetting votes to 0\n"
if
(
$bug_fields
{
'votes'
}
);
$status
=
$initial_status
;
}
}
...
...
process_bug.cgi
View file @
120b63d5
...
...
@@ -573,27 +573,12 @@ foreach my $bug (@bug_objects) {
# an error later.
delete
$changed_deps
{
''
};
# @msgs will store emails which have to be sent to voters, if any.
my
@msgs
;
if
(
$changes
->
{
'product'
})
{
# If some votes have been removed, RemoveVotes() returns
# a list of messages to send to voters.
# We delay the sending of these messages till changes are committed.
@msgs
=
RemoveVotes
(
$bug
->
id
,
0
,
'votes_bug_moved'
);
CheckIfVotedConfirmed
(
$bug
->
id
);
}
$dbh
->
bz_commit_transaction
();
###############
# Send Emails #
###############
# Now is a good time to send email to voters.
foreach
my
$msg
(
@msgs
)
{
MessageToMTA
(
$msg
);
}
my
$old_qa
=
$changes
->
{
'qa_contact'
}
?
$changes
->
{
'qa_contact'
}
->
[
0
]
:
''
;
my
$old_own
=
$changes
->
{
'assigned_to'
}
?
$changes
->
{
'assigned_to'
}
->
[
0
]
:
''
;
my
$old_cc
=
$changes
->
{
cc
}
?
$changes
->
{
cc
}
->
[
0
]
:
''
;
...
...
query.cgi
View file @
120b63d5
...
...
@@ -127,7 +127,7 @@ sub PrefillForm {
"email"
,
"emailtype"
,
"emailreporter"
,
"emailassigned_to"
,
"emailcc"
,
"emailqa_contact"
,
"emaillongdesc"
,
"content"
,
"changedin"
,
"
votes"
,
"
short_desc"
,
"short_desc_type"
,
"changedin"
,
"short_desc"
,
"short_desc_type"
,
"longdesc"
,
"longdesc_type"
,
"bug_file_loc"
,
"bug_file_loc_type"
,
"status_whiteboard"
,
"status_whiteboard_type"
,
"bug_id"
,
...
...
report.cgi
View file @
120b63d5
...
...
@@ -113,7 +113,6 @@ my @columns = qw(
qa_contact
classification
version
votes
keywords
target_milestone
)
;
...
...
sanitycheck.cgi
View file @
120b63d5
...
...
@@ -102,7 +102,7 @@ unless (Bugzilla->usage_mode == USAGE_MODE_CMDLINE) {
# Users with 'editkeywords' privs only can only check keywords.
###########################################################################
unless
(
$user
->
in_group
(
'editcomponents'
))
{
check_
votes_or_keywords
(
'keywords'
);
check_
keywords
(
);
Status
(
'checks_completed'
);
$template
->
process
(
'global/footer.html.tmpl'
,
$vars
)
...
...
@@ -111,27 +111,6 @@ unless ($user->in_group('editcomponents')) {
}
###########################################################################
# Fix vote cache
###########################################################################
if
(
$cgi
->
param
(
'rebuildvotecache'
))
{
Status
(
'vote_cache_rebuild_start'
);
$dbh
->
bz_start_transaction
();
$dbh
->
do
(
q{UPDATE bugs SET votes = 0}
);
my
$sth_update
=
$dbh
->
prepare
(
q{UPDATE bugs
SET votes = ?
WHERE bug_id = ?}
);
my
$sth
=
$dbh
->
prepare
(
q{SELECT bug_id, SUM(vote_count)
FROM votes }
.
$dbh
->
sql_group_by
(
'bug_id'
));
$sth
->
execute
();
while
(
my
(
$id
,
$v
)
=
$sth
->
fetchrow_array
)
{
$sth_update
->
execute
(
$v
,
$id
);
}
$dbh
->
bz_commit_transaction
();
Status
(
'vote_cache_rebuild_end'
);
}
###########################################################################
# Create missing group_control_map entries
###########################################################################
...
...
@@ -310,7 +289,7 @@ if ($cgi->param('remove_invalid_bug_references')) {
'bugs_fulltext/'
,
'cc/'
,
'dependencies/blocked'
,
'dependencies/dependson'
,
'duplicates/dupe'
,
'duplicates/dupe_of'
,
'flags/'
,
'keywords/'
,
'longdescs/'
,
'votes/'
)
{
'flags/'
,
'keywords/'
,
'longdescs/'
)
{
my
(
$table
,
$field
)
=
split
(
'/'
,
$pair
);
$field
||=
"bug_id"
;
...
...
@@ -489,7 +468,6 @@ CrossCheck("bugs", "bug_id",
[
"dependencies"
,
"blocked"
],
[
"dependencies"
,
"dependson"
],
[
'flags'
,
'bug_id'
],
[
"votes"
,
"bug_id"
],
[
"keywords"
,
"bug_id"
],
[
"duplicates"
,
"dupe_of"
,
"dupe"
],
[
"duplicates"
,
"dupe"
,
"dupe_of"
]);
...
...
@@ -524,7 +502,6 @@ CrossCheck("profiles", "userid",
[
"bugs_activity"
,
"who"
,
"bug_id"
],
[
"cc"
,
"who"
,
"bug_id"
],
[
'quips'
,
'userid'
],
[
"votes"
,
"who"
,
"bug_id"
],
[
"longdescs"
,
"who"
,
"bug_id"
],
[
"logincookies"
,
"userid"
],
[
"namedqueries"
,
"userid"
],
...
...
@@ -681,75 +658,19 @@ while (my ($id, $email) = $sth->fetchrow_array) {
}
###########################################################################
# Perform
vote/
keyword cache checks
# Perform keyword cache checks
###########################################################################
check_votes_or_keywords
();
sub
check_votes_or_keywords
{
my
$check
=
shift
||
'all'
;
sub
check_keywords
{
my
$dbh
=
Bugzilla
->
dbh
;
my
$sth
=
$dbh
->
prepare
(
q{SELECT bug_id, votes, keywords
FROM bugs
WHERE votes != 0 OR keywords != ''}
);
$sth
->
execute
;
my
%
votes
;
my
%
keyword
;
while
(
my
(
$id
,
$v
,
$k
)
=
$sth
->
fetchrow_array
)
{
if
(
$v
!=
0
)
{
$votes
{
$id
}
=
$v
;
}
if
(
$k
)
{
$keyword
{
$id
}
=
$k
;
}
}
# If we only want to check keywords, skip checks about votes.
_check_votes
(
\%
votes
)
unless
(
$check
eq
'keywords'
);
# If we only want to check votes, skip checks about keywords.
_check_keywords
(
\%
keyword
)
unless
(
$check
eq
'votes'
);
}
sub
_check_votes
{
my
$votes
=
shift
;
Status
(
'vote_count_start'
);
my
$dbh
=
Bugzilla
->
dbh
;
my
$sth
=
$dbh
->
prepare
(
q{SELECT bug_id, SUM(vote_count)
FROM votes }
.
$dbh
->
sql_group_by
(
'bug_id'
));
$sth
->
execute
;
my
$offer_votecache_rebuild
=
0
;
while
(
my
(
$id
,
$v
)
=
$sth
->
fetchrow_array
)
{
if
(
$v
<=
0
)
{
Status
(
'vote_count_alert'
,
{
id
=>
$id
},
'alert'
);
}
else
{
if
(
!
defined
$votes
->
{
$id
}
||
$votes
->
{
$id
}
!=
$v
)
{
Status
(
'vote_cache_alert'
,
{
id
=>
$id
},
'alert'
);
$offer_votecache_rebuild
=
1
;
}
delete
$votes
->
{
$id
};
}
}
foreach
my
$id
(
keys
%
$votes
)
{
Status
(
'vote_cache_alert'
,
{
id
=>
$id
},
'alert'
);
$offer_votecache_rebuild
=
1
;
}
my
$cgi
=
Bugzilla
->
cgi
;
Status
(
'vote_cache_rebuild_fix'
)
if
$offer_votecache_rebuild
;
}
my
%
keyword
=
@
{
$dbh
->
selectcol_arrayref
(
q{SELECT bug_id, keywords FROM bugs WHERE keywords != ''}
,
{
Columns
=>
[
1
,
2
]})
};
sub
_check_keywords
{
my
$keyword
=
shift
;
Status
(
'keyword_check_start'
);
my
$dbh
=
Bugzilla
->
dbh
;
my
$cgi
=
Bugzilla
->
cgi
;
my
%
keywordids
;
my
$keywords
=
$dbh
->
selectall_arrayref
(
q{SELECT id, name
...
...
@@ -819,13 +740,13 @@ sub _check_keywords {
my
@badbugs
=
();
foreach
my
$b
(
keys
(
%
$
keyword
))
{
if
(
!
exists
$realk
{
$b
}
||
$realk
{
$b
}
ne
$keyword
->
{
$b
})
{
foreach
my
$b
(
keys
(
%
keyword
))
{
if
(
!
exists
$realk
{
$b
}
||
$realk
{
$b
}
ne
$keyword
{
$b
})
{
push
(
@badbugs
,
$b
);
}
}
foreach
my
$b
(
keys
(
%
realk
))
{
if
(
!
exists
$keyword
->
{
$b
})
{
if
(
!
exists
$keyword
{
$b
})
{
push
(
@badbugs
,
$b
);
}
}
...
...
@@ -973,13 +894,6 @@ my $confirmed_open_states = join(', ', map {$dbh->quote($_)} @confirmed_open_sta
BugCheck
(
"bugs WHERE bug_status IN ($confirmed_open_states) AND everconfirmed = 0"
,
'bug_check_status_everconfirmed_error_text2'
,
'repair_everconfirmed'
);
Status
(
'bug_check_votes_everconfirmed'
);
BugCheck
(
"bugs INNER JOIN products ON bugs.product_id = products.id "
.
"WHERE everconfirmed = 0 AND votestoconfirm > 0
AND votestoconfirm <= votes"
,
'bug_check_votes_everconfirmed_error_text'
);
###########################################################################
# Control Values
###########################################################################
...
...
skins/standard/show_bug.css
View file @
120b63d5
...
...
@@ -54,9 +54,8 @@ table#flags {
height
:
1em
;
}
#duplicate_settings
,
#votes_container
{
#duplicate_settings
{
white-space
:
nowrap
;
}
#bz_big_form_parts
td
{
...
...
template/en/default/account/prefs/email.html.tmpl
View file @
120b63d5
...
...
@@ -35,9 +35,6 @@
[% PROCESS global/variables.none.tmpl %]
[% useqacontact = Param('useqacontact') %]
[% usevotes = Param('usevotes') %]
<p>
If you don't like getting a notification for "trivial"
changes to [% terms.bugs %], you can use the settings below to
...
...
@@ -150,21 +147,28 @@ document.write('<input type="button" value="Disable All Mail" onclick="SetCheckb
[% relationships = [
{ id = constants.REL_ASSIGNEE,
description = "Assignee" },
{ id = constants.REL_QA,
description = "QA Contact" },
{ id = constants.REL_REPORTER,
description = "Reporter" },
{ id = constants.REL_CC,
description = "CCed" },
{ id = constants.REL_VOTER,
description = "Voter" },
] %]
[% IF Param('useqacontact') %]
[% relationships.push({ id = constants.REL_QA,
description = "QA Contact" }) %]
[% END %]
[%# This is up here so that the "relationships" hook can modify it. %]
[% no_added_removed = [constants.REL_REPORTER] %]
[% Hook.process('relationships') %]
[% num_columns = relationships.size %]
<table class="bz_emailprefs" border="1">
<tr>
<td colspan="[% (useqacontact AND usevotes) ? '5' :
((useqacontact OR usevotes) ? '4' : '3') %]"
align="center" width="50%">
<td colspan="[% num_columns FILTER html %]" align="center" width="50%">
<b>When my relationship to this [% terms.bug %] is:</b>
</td>
<td rowspan="2" width="40%">
...
...
@@ -174,8 +178,6 @@ document.write('<input type="button" value="Disable All Mail" onclick="SetCheckb
<tr>
[% FOREACH relationship = relationships %]
[% NEXT IF (relationship.id == constants.REL_QA AND NOT useqacontact) OR
(relationship.id == constants.REL_VOTER AND NOT usevotes) %]
<th align="center" width="9%">
[% relationship.description FILTER html %]
</th>
...
...
@@ -186,16 +188,14 @@ document.write('<input type="button" value="Disable All Mail" onclick="SetCheckb
[% count = loop.count() %]
<tr class="bz_row_[% count % 2 == 1 ? "odd" : "even" %]">
[% FOREACH relationship = relationships %]
[% NEXT IF (relationship.id == constants.REL_QA AND NOT useqacontact) OR
(relationship.id == constants.REL_VOTER AND NOT usevotes) %]
<td align="center">
<input type="checkbox"
name="email-[% relationship.id %]-[% event.id %]"
value="1"
[%# The combinations don't always make sense; disable a couple %]
[% IF event.id == constants.EVT_ADDED_REMOVED AND
(relationship.id == constants.REL_REPORTER OR
relationship.id == constants.REL_VOTER)
%]
no_added_removed.contains(relationship.id)
%]
disabled
[% ELSIF mail.${relationship.id}.${event.id} %]
checked
...
...
@@ -209,8 +209,7 @@ document.write('<input type="button" value="Disable All Mail" onclick="SetCheckb
[% END %]
<tr>
<td colspan="[% (useqacontact AND usevotes) ? '5' :
((useqacontact OR usevotes) ? '4' : '3') %]"
<td colspan="[% num_columns FILTER html %]"
align="center" width="50%">
</td>
...
...
@@ -223,8 +222,6 @@ document.write('<input type="button" value="Disable All Mail" onclick="SetCheckb
[% count = loop.count() %]
<tr class="bz_row_[% count % 2 == 1 ? "odd" : "even" %]">
[% FOREACH relationship = relationships %]
[% NEXT IF (relationship.id == constants.REL_QA AND NOT useqacontact) OR
(relationship.id == constants.REL_VOTER AND NOT usevotes) %]
<td align="center">
<input type="checkbox"
name="neg-email-[% relationship.id %]-[% event.id %]"
...
...
@@ -243,23 +240,17 @@ document.write('<input type="button" value="Disable All Mail" onclick="SetCheckb
[%# Add hidden form fields for fields not used %]
[% FOREACH event = events %]
[% FOREACH relationship = relationships %]
[% IF (relationship.id == constants.REL_QA AND NOT useqacontact) OR
(relationship.id == constants.REL_VOTER AND NOT usevotes) %]
<input type="hidden"
name="email-[% relationship.id %]-[% event.id %]"
value="[% mail.${relationship.id}.${event.id} ? "1" : "0" %]">
[% END %]
<input type="hidden"
name="email-[% relationship.id %]-[% event.id %]"
value="[% mail.${relationship.id}.${event.id} ? "1" : "0" %]">
[% END %]
[% END %]
[% FOREACH event = neg_events %]
[% FOREACH relationship = relationships %]
[% IF (relationship.id == constants.REL_QA AND NOT useqacontact) OR
(relationship.id == constants.REL_VOTER AND NOT usevotes) %]
<input type="hidden"
name="neg-email-[% relationship.id %]-[% event.id %]"
value="[% mail.${relationship.id}.${event.id} ? "0" : "1" %]">
[% END %]
<input type="hidden"
name="neg-email-[% relationship.id %]-[% event.id %]"
value="[% mail.${relationship.id}.${event.id} ? "0" : "1" %]">
[% END %]
[% END %]
...
...
template/en/default/admin/params/bugfields.html.tmpl
View file @
120b63d5
...
...
@@ -34,11 +34,6 @@
usestatuswhiteboard => "Do you wish to use the Status Whiteboard field?",
usevotes => "Do you wish to allow users to vote for ${terms.bugs}? Note that in order " _
"for this to be effective, you will have to change the maximum " _
"votes allowed in a product to be non-zero in " _
"<a href=\"editproducts.cgi\">the product edit page</a>.",
usebugaliases => "Do you wish to use $terms.bug aliases, which allow you to assign " _
"$terms.bugs an easy-to-remember name by which you can refer to them?",
...
...
template/en/default/admin/products/create.html.tmpl
View file @
120b63d5
...
...
@@ -29,9 +29,6 @@
%]
[% DEFAULT
product.votesperuser = "0",
product.maxvotesperbug = "10000",
product.votes_to_confirm = "0",
product.is_active = 1,
version = "unspecified",
product.defaultmilestone = constants.DEFAULT_MILESTONE
...
...
template/en/default/admin/products/edit-common.html.tmpl
View file @
120b63d5
...
...
@@ -76,37 +76,8 @@
in this product:</label>
</th>
<td><input type="checkbox" id="allows_unconfirmed" name="allows_unconfirmed"
[% ' checked="checked"' IF product.allows_unconfirmed %]
[% IF Param('usevotes') %]
onchange="bz_toggleClass('votes_to_confirm_container',
'bz_default_hidden')"
[% END %]>
[% IF Param('usevotes') %]
<span id="votes_to_confirm_container"
[% ' class="bz_default_hidden"' IF !product.allows_unconfirmed %]>
...and automatically confirm [% terms.bugs %] if they get
<input size="3" maxlength="5" name="votestoconfirm" id="votestoconfirm"
value="[% product.votes_to_confirm FILTER html %]">
votes. (Setting this to 0 disables auto-confirming [% terms.bugs %]
by vote.)
</span>
[% END %]
[% ' checked="checked"' IF product.allows_unconfirmed %]>
</td>
</tr>
[% IF Param('usevotes') %]
<tr>
<th align="right">Maximum votes per person:</th>
<td><input size="5" maxlength="5" name="votesperuser" id="votesperuser"
value="[% product.votesperuser FILTER html %]">
</td>
</tr>
<tr>
<th align="right">
Maximum votes a person can put on a single [% terms.bug %]:
</th>
<td><input size="5" maxlength="5" name="maxvotesperbug" id="maxvotesperbug"
value="[% product.maxvotesperbug FILTER html %]">
</td>
</tr>
[% END %]
[% Hook.process('rows') %]
template/en/default/admin/products/list.html.tmpl
View file @
120b63d5
...
...
@@ -64,22 +64,7 @@
heading => "Open For New $terms.Bugs"
yesno_field => 1
},
{
name => "votesperuser"
heading => "Votes Per User"
align => 'right'
},
{
name => "maxvotesperbug"
heading => "Maximum Votes Per $terms.Bug"
align => 'right'
},
{
name => "votestoconfirm"
heading => "Votes To Confirm"
align => 'right'
} ]
%]
] %]
[% IF showbugcounts %]
...
...
template/en/default/admin/products/updated.html.tmpl
View file @
120b63d5
...
...
@@ -75,33 +75,6 @@
'[% product.default_milestone FILTER html %]'.
</p>
[% END %]
[% IF changes.votesperuser.defined %]
<p>
Updated votes per user from
[%+ changes.votesperuser.0 FILTER html %] to
[%+ product.votes_per_user FILTER html %].
</p>
[% checkvotes = 1 %]
[% END %]
[% IF changes.maxvotesperbug.defined %]
<p>
Updated maximum votes per [% terms.bug %] from
[%+ changes.maxvotesperbug.0 FILTER html %] to
[%+ product.max_votes_per_bug FILTER html %].
</p>
[% checkvotes = 1 %]
[% END %]
[% IF changes.votestoconfirm.defined %]
<p>
Updated number of votes needed to confirm a [% terms.bug %] from
[%+ changes.votestoconfirm.0 FILTER html %] to
[%+ product.votes_to_confirm FILTER html %].
</p>
[% checkvotes = 1 %]
[% END %]
[% IF changes.allows_unconfirmed.defined %]
<p>
...
...
@@ -121,65 +94,12 @@
</p>
[% END %]
[% Hook.process('changes') %]
[% IF !changes.keys.size %]
<p>Nothing changed for product '[% product.name FILTER html %]'.</p>
[% END %]
[%# Note that this display of changed votes and/or confirmed bugs is
not very scalable. We could have a _lot_, and we just list them all.
One day we should limit this perhaps, or have a more scalable display %]
[% IF checkvotes %]
<hr>
<p>Checking existing votes in this product for anybody who now
has too many votes for [% terms.abug %]...<br>
[% IF changes.too_many_votes.size %]
[% FOREACH detail = changes.too_many_votes %]
→removed votes for [% terms.bug %] <a href="show_bug.cgi?id=
[%- detail.id FILTER url_quote %]">
[%- detail.id FILTER html %]</a> from [% detail.name FILTER html %]<br>
[% END %]
[% ELSE %]
→there were none.
[% END %]
</p>
<p>Checking existing votes in this product for anybody
who now has too many total votes...<br>
[% IF changes.too_many_total_votes.size %]
[% FOREACH detail = changes.too_many_total_votes %]
→removed votes for [% terms.bug %] <a href="show_bug.cgi?id=
[%- detail.id FILTER url_quote %]">
[%- detail.id FILTER html %]</a> from [% detail.name FILTER html %]<br>
[% END %]
[% ELSE %]
→there were none.
[% END %]
</p>
<p>Checking unconfirmed [% terms.bugs %] in this product for any which now have
sufficient votes...<br>
[% IF changes.confirmed_bugs.size %]
[% FOREACH id = changes.confirmed_bugs %]
[%# This is INCLUDED instead of PROCESSED to avoid variables getting
overwritten, which happens otherwise %]
[% INCLUDE bug/process/results.html.tmpl
type = 'votes'
mailrecipients = { 'changer' => user.login }
header_done = 1
id = id
%]
[% END %]
[% ELSE %]
→there were none.
[% END %]
</p>
[% END %]
[% PROCESS admin/products/footer.html.tmpl %]
[% PROCESS global/footer.html.tmpl %]
template/en/default/admin/sanitycheck/messages.html.tmpl
View file @
120b63d5
...
...
@@ -81,12 +81,6 @@
[% ELSIF san_tag == "bug_check_status_everconfirmed_error_text2" %]
[% terms.Bugs %] with confirmed status but don't have everconfirmed set
[% ELSIF san_tag == "bug_check_votes_everconfirmed" %]
Checking votes/everconfirmed
[% ELSIF san_tag == "bug_check_votes_everconfirmed_error_text" %]
[% terms.Bugs %] that have enough votes to be confirmed but haven't been
[% ELSIF san_tag == "bug_check_control_values" %]
Checking for bad values in group_control_map
...
...
@@ -275,25 +269,6 @@
[% ELSIF san_tag == "unsent_bugmail_fix" %]
<a href="sanitycheck.cgi?rescanallBugMail=1">Send these mails</a>.
[% ELSIF san_tag == "vote_cache_rebuild_start" %]
OK, now rebuilding vote cache.
[% ELSIF san_tag == "vote_cache_rebuild_end" %]
Vote cache has been rebuilt.
[% ELSIF san_tag == "vote_cache_rebuild_fix" %]
<a href="sanitycheck.cgi?rebuildvotecache=1">Click here to
rebuild the vote cache</a>
[% ELSIF san_tag == "vote_cache_alert" %]
Bad vote cache for [% PROCESS bug_link bug_id = id %]
[% ELSIF san_tag == "vote_count_start" %]
Checking cached vote counts.
[% ELSIF san_tag == "vote_count_alert" %]
Bad vote sum for [% terms.bug %] [%+ id FILTER html %].
[% ELSIF san_tag == "whines_obsolete_target_deletion_start" %]
OK, now removing non-existent users/groups from whines.
...
...
template/en/default/admin/users/confirm-delete.html.tmpl
View file @
120b63d5
...
...
@@ -33,7 +33,6 @@
# namedquery_group_map: number of named queries the user has shared
# profiles_activity: number of changes made to other users' profiles
# series: number of series the viewed user has created
# votes: number of bugs the viewed user has voted on
# watch.watched: number of users the viewed user is being watched
# by
# watch.watcher: number of users the viewed user is watching
...
...
@@ -226,8 +225,8 @@
[% END %]
[% IF assignee_or_qa || cc || component_cc || email_setting || flags.requestee ||
namedqueries || profile_setting || quips || series ||
votes ||
watch.watched ||
watch.watcher || whine_events || whine_schedules %]
namedqueries || profile_setting || quips || series || watch.watched ||
watch.watcher || whine_events || whine_schedules
|| other_safe
%]
<div class="warningmessages">
<p>The following deletions are <b>safe</b> and will not generate
referential integrity inconsistencies.</p>
...
...
@@ -372,23 +371,6 @@
will have no author anymore, but will remain available.
</li>
[% END %]
[% IF votes %]
<li>
[% otheruser.login FILTER html %] has voted on
[% IF votes == 1 %]
[%+ terms.abug %]
[% ELSE %]
[%+ votes %] [%+ terms.bugs %]
[% END %].
If you delete the user account,
[% IF votes == 1 %]
this vote
[% ELSE %]
these votes
[% END %]
will be deleted along with the user account.
</li>
[% END %]
[% IF watch.watched || watch.watcher %]
<li>
[% otheruser.login FILTER html %]
...
...
@@ -445,6 +427,7 @@
but the whines themselves will be left unaltered.
</li>
[% END %]
[% Hook.process('warn_safe') %]
</ul>
</div>
...
...
template/en/default/bug/edit.html.tmpl
View file @
120b63d5
...
...
@@ -398,7 +398,7 @@
[% BLOCK section_details2 %]
[%###############################################################%]
[%# Importance (priority
, severity and votes
) #%]
[%# Importance (priority
and severity
) #%]
[%###############################################################%]
<tr>
<td class="field_label">
...
...
@@ -414,22 +414,7 @@
bug = bug, field = bug_fields.bug_severity,
no_tds = 1, value = bug.bug_severity
editable = bug.check_can_change_field('bug_severity', 0, 1) %]
[% IF bug.use_votes %]
<span id="votes_container">
[% IF bug.votes %]
with
<a href="votes.cgi?action=show_bug&bug_id=[% bug.bug_id %]">
[% bug.votes %]
[% IF bug.votes == 1 %]
vote
[% ELSE %]
votes
[% END %]</a>
[% END %]
(<a href="votes.cgi?action=show_user&bug_id=
[% bug.bug_id %]#vote_[% bug.bug_id %]">vote</a>)
</span>
[% END %]
[% Hook.process('after_importance', 'bug/edit.html.tmpl') %]
</td>
</tr>
...
...
template/en/default/bug/format_comment.txt.tmpl
View file @
120b63d5
...
...
@@ -39,8 +39,6 @@ X[% comment_body %]
*** This [% terms.bug %] has been marked as a duplicate of [% terms.bug %] [%+ comment.extra_data %] ***
[% ELSIF comment.type == constants.CMT_HAS_DUPE %]
*** [% terms.Bug %] [%+ comment.extra_data %] has been marked as a duplicate of this [% terms.bug %]. ***
[% ELSIF comment.type == constants.CMT_POPULAR_VOTES %]
*** This [% terms.bug %] has been confirmed by popular vote. ***
[% ELSIF comment.type == constants.CMT_MOVED_TO %]
X[% comment_body %]
...
...
@@ -65,6 +63,8 @@ Comment on attachment [% comment.extra_data %]
[%+ comment.attachment.description %]
[%+ comment.body %]
[% ELSIF comment.type %]
[% Hook.process('type') %]
[% ELSE %]
X[% comment_body %]
[% END %]
template/en/default/bug/process/header.html.tmpl
View file @
120b63d5
...
...
@@ -39,8 +39,8 @@
[% END %]
[% ELSIF title_tag == "mid_air" %]
[% title = "Mid-air collision!" %]
[% ELSIF title_tag == "change_votes" %]
[% title = "Change Votes" %]
[% END %]
[% Hook.process('title') %]
[% PROCESS global/header.html.tmpl %]
template/en/default/bug/process/results.html.tmpl
View file @
120b63d5
...
...
@@ -44,12 +44,13 @@
'bug' => "Changes submitted for $link" ,
'dupe' => "Duplicate notation added to $link" ,
'dep' => "Checking for dependency changes on $link" ,
'votes' => "$Link confirmed by number of votes" ,
'created' => "$Link has been added to the database" ,
'move' => "$Link has been moved to another database" ,
}
%]
[% Hook.process('title') %]
<dl>
<dt>[% title.$type %]</dt>
<dd>
...
...
template/en/default/email/newchangedmail.txt.tmpl
View file @
120b63d5
...
...
@@ -19,6 +19,8 @@
#%]
[% PROCESS "global/variables.none.tmpl" %]
[% PROCESS "global/reason-descs.none.tmpl" %]
From: [% Param('mailfrom') %]
To: [% to_user.email %]
Subject: [[% terms.Bug %] [%+ bugid %]] [% 'New: ' IF isnew %][%+ summary %]
...
...
@@ -56,33 +58,12 @@ X-Bugzilla-Changed-Fields: [% changedfields %]
-- [%# Protect the trailing space of the signature marker %]
Configure [% terms.bug %]mail: [% urlbase %]userprefs.cgi?tab=email
------- You are receiving this mail because: -------
[% FOREACH relationship = reasons %]
[% SWITCH relationship %]
[% CASE constants.REL_ASSIGNEE %]
You are the assignee for the [% terms.bug %].
[% CASE constants.REL_REPORTER %]
You reported the [% terms.bug %].
[% CASE constants.REL_QA %]
You are the QA contact for the [% terms.bug %].
[% CASE constants.REL_CC %]
You are on the CC list for the [% terms.bug %].
[% CASE constants.REL_VOTER %]
You are a voter for the [% terms.bug %].
[% CASE constants.REL_GLOBAL_WATCHER %]
You are watching all [% terms.bug %] changes.
[% END %]
[% SET reason_lines = [] %]
[% FOREACH reason = reasons %]
[% reason_lines.push(reason_descs.$reason) IF reason_descs.$reason %]
[% END %]
[% FOREACH relationship = reasons_watch %]
[% SWITCH relationship %]
[% CASE constants.REL_ASSIGNEE %]
You are watching the assignee of the [% terms.bug %].
[% CASE constants.REL_REPORTER %]
You are watching the reporter.
[% CASE constants.REL_QA %]
You are watching the QA contact of the [% terms.bug %].
[% CASE constants.REL_CC %]
You are watching someone on the CC list of the [% terms.bug %].
[% CASE constants.REL_VOTER %]
You are watching a voter for the [% terms.bug %].
[% END %]
[% FOREACH reason = reasons_watch %]
[% reason_lines.push(watch_reason_descs.$reason)
IF watch_reason_descs.$reason %]
[% END %]
[%+ reason_lines.join("\n") %]
template/en/default/filterexceptions.pl
View file @
120b63d5
...
...
@@ -233,7 +233,6 @@
'global/site-navigation.html.tmpl'
=>
[
'bug.bug_id'
,
'bug.votes'
,
],
'bug/comments.html.tmpl'
=>
[
...
...
@@ -264,7 +263,6 @@
'bug.remaining_time'
,
'bug.delta_ts'
,
'bug.bug_id'
,
'bug.votes'
,
'group.bit'
,
'dep.title'
,
'dep.fieldname'
,
...
...
@@ -312,19 +310,6 @@
FILTER format("%d")'
,
],
'bug/votes/list-for-bug.html.tmpl'
=>
[
'voter.vote_count'
,
'total'
,
],
'bug/votes/list-for-user.html.tmpl'
=>
[
'product.maxperbug'
,
'bug.id'
,
'bug.count'
,
'product.total'
,
'product.maxvotes'
,
],
'bug/process/results.html.tmpl'
=>
[
'title.$type'
,
'"$terms.Bug $id" FILTER bug_link(id)'
,
...
...
@@ -482,7 +467,6 @@
'flags.setter'
,
'longdescs'
,
'quips'
,
'votes'
,
'series'
,
'watch.watched'
,
'watch.watcher'
,
...
...
template/en/default/global/field-descs.none.tmpl
View file @
120b63d5
...
...
@@ -84,7 +84,6 @@
"status_whiteboard" => "Whiteboard",
"target_milestone" => "Target Milestone",
"version" => "Version",
"votes" => "Votes",
"work_time" => "Hours Worked"} %]
[%# Also include any custom fields or fields which don't have a
...
...
template/en/default/global/reason-descs.none.tmpl
0 → 100644
View file @
120b63d5
[%# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# The Initial Developer of the Original Code is Everything Solved, Inc.
# Portions created by the Initial Developer are Copyright (C) 2010
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Max Kanat-Alexander <mkanat@bugzilla.org>
#%]
[% SET reason_descs = {
${constants.REL_ASSIGNEE} => "You are the assignee for the ${terms.bug}.",
${constants.REL_REPORTER} => "You reported the ${terms.bug}.",
${constants.REL_QA} => "You are the QA Contact for the ${terms.bug}.",
${constants.REL_CC} => "You are on the CC list for the ${terms.bug}.",
${constants.REL_GLOBAL_WATCHER} => "You are watching all $terms.bug changes.",
} %]
[% SET watch_reason_descs => {
${constants.REL_ASSIGNEE} =>
"You are the watching assignee of the ${terms.bug}.",
${constants.REL_REPORTER} =>
"You watching the reporter of the ${terms.bug}.",
${constants.REL_QA} =>
"You are watching the QA Contact of the ${terms.bug}.",
${constants.REL_CC} =>
"You are watching someone on the CC list of the ${terms.bug}.",
} %]
[% Hook.process('end') %]
template/en/default/global/site-navigation.html.tmpl
View file @
120b63d5
...
...
@@ -37,7 +37,7 @@
[% END %]
[%# *** Dependencies,
Votes,
Activity, Print-version *** %]
[%# *** Dependencies, Activity, Print-version *** %]
[% IF bug %]
<link rel="Show" title="Dependency Tree"
href="showdependencytree.cgi?id=[% bug.bug_id %]&hide_resolved=1">
...
...
@@ -46,11 +46,6 @@
href="showdependencygraph.cgi?id=[% bug.bug_id %]">
[% END %]
[% IF bug.use_votes %]
<link rel="Show" title="Votes ([% bug.votes %])"
href="votes.cgi?action=show_bug&bug_id=[% bug.bug_id %]">
[% END %]
<link rel="Show" title="[% terms.Bug %] Activity"
href="show_activity.cgi?id=[% bug.bug_id %]">
<link rel="Show" title="Printer-Friendly Version"
...
...
template/en/default/global/user-error.html.tmpl
View file @
120b63d5
...
...
@@ -740,11 +740,6 @@
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.
You entered <tt>[% value FILTER html %]</tt>, which isn't.
[% ELSIF error == "illegal_attachment_edit" %]
[% title = "Unauthorized Action" %]
You are not authorized to edit attachment [% attach_id FILTER html %].
...
...
@@ -1318,20 +1313,6 @@
[% group.name FILTER html %] is not an active [% terms.bug %] group
and so you cannot edit group controls for it.
[% ELSIF error == "product_illegal_votes" %]
[% title = "Votes Must Be Non-negative" %]
[% admindocslinks = {'voting.html' => 'Setting up the voting feature'} %]
'[% votes FILTER html %]' is an invalid value for the
<em>
[% IF field == "votesperuser" %]
Votes Per User
[% ELSIF field == "maxvotesperbug" %]
Maximum Votes Per [% terms.Bug %]
[% ELSIF field == "votestoconfirm" %]
Votes To Confirm
[% END %]
</em> field, which should contain a non-negative number.
[% ELSIF error == "product_name_already_in_use" %]
[% title = "Product name already exists" %]
[% admindocslinks = {'products.html' => 'Administering products'} %]
...
...
@@ -1548,21 +1529,6 @@
[% title = "User Protected" %]
The user [% login FILTER html %] may not be impersonated by sudoers.
[% ELSIF error == "too_many_votes_for_bug" %]
[% title = "Illegal Vote" %]
[% admindocslinks = {'voting.html' => 'Setting up the voting feature'} %]
You may only use at most [% max FILTER html %] votes for a single
[%+ terms.bug %] in the
<tt>[% product FILTER html %]</tt> product, but you are trying to
use [% votes FILTER html %].
[% ELSIF error == "too_many_votes_for_product" %]
[% title = "Illegal Vote" %]
[% admindocslinks = {'voting.html' => 'Setting up the voting feature'} %]
You tried to use [% votes FILTER html %] votes in the
<tt>[% product FILTER html %]</tt> product, which exceeds the maximum of
[%+ max FILTER html %] votes for this product.
[% ELSIF error == "token_does_not_exist" %]
[% title = "Token Does Not Exist" %]
The token you submitted does not exist, has expired, or has
...
...
@@ -1650,11 +1616,6 @@
Sorry, but you are not allowed to (un)mark comments or attachments
as private.
[% ELSIF error == "votes_must_be_nonnegative" %]
[% title = "Votes Must Be Non-negative" %]
[% admindocslinks = {'voting.html' => 'Setting up the voting feature'} %]
Only use non-negative numbers for your [% terms.bug %] votes.
[% ELSIF error == "wrong_token_for_cancelling_email_change" %]
[% title = "Wrong Token" %]
That token cannot be used to cancel an email address change.
...
...
template/en/default/list/list.rdf.tmpl
View file @
120b63d5
...
...
@@ -38,7 +38,7 @@
<bz:id nc:parseType="Integer">[% bug.bug_id %]</bz:id>
[% FOREACH column = displaycolumns %]
<bz:[% column %]
[% ' nc:parseType="Integer"' IF column == "votes" %]
>[% bug.$column FILTER html %]</bz:[% column %]>
<bz:[% column %]>[% bug.$column FILTER html %]</bz:[% column %]>
[% END %]
</bz:bug>
...
...
template/en/default/search/form.html.tmpl
View file @
120b63d5
...
...
@@ -422,20 +422,14 @@ function doOnSelectProduct(selectmode) {
</tr>
</table>
[%# *** Email Numbering
Votes
*** %]
[%# *** Email Numbering *** %]
<table>
<tr>
<td>
<fieldset>
<legend>
<strong>
[% IF Param('usevotes') %]
Email Addresses, [% terms.Bug %] Numbers, and Votes
[% ELSE %]
Email Addresses and [% terms.Bug %] Numbers
[% END %]
</strong>
<strong>Email Addresses and [% terms.Bug %] Numbers</strong>
</legend>
...
...
@@ -550,18 +544,7 @@ function doOnSelectProduct(selectmode) {
<td></td>
<td>(comma-separated list)</td>
</tr>
[% IF Param('usevotes') %]
<tr>
<td align="right">
<label for="votes">Only [% terms.bugs %] with at least</label>:
</td>
<td>
<input name="votes" id="votes" size="3"
value="[% default.votes.0 FILTER html %]">
votes
</td>
</tr>
[% END %]
[% Hook.process('email_numbering_end') %]
</table>
...
...
template/en/default/search/search-help.html.tmpl
View file @
120b63d5
...
...
@@ -82,9 +82,6 @@
roles.<br>Here, you can search on what people are in what role." },
{ id => "bug_id",
html => "You can limit your search to a specific set of $terms.bugs ." },
{ id => "votes",
html => "Some $terms.bugs can be voted for, and you can limit your search to
$terms.bugs<br>with more than a certain number of votes." },
{ id => "chfield",
html => "You can search for specific types of change - this field define <br>
which field you are interested in changes for." },
...
...
template/en/default/search/search-report-select.html.tmpl
View file @
120b63d5
...
...
@@ -29,7 +29,8 @@
[% rep_fields = ["classification", "product", "component", "version", "rep_platform",
"op_sys", "bug_status", "resolution", "bug_severity",
"priority", "target_milestone", "assigned_to",
"reporter", "qa_contact", "votes" ] %]
"reporter", "qa_contact" ] %]
[% Hook.process('rep_fields', 'search/search-report-select.html.tmpl') %]
<select name="[% name FILTER html %]">
<option value=""><none></option>
...
...
@@ -38,7 +39,6 @@
[% NEXT IF field == "classification" AND !Param('useclassification') %]
[% NEXT IF field == "target_milestone" AND !Param('usetargetmilestone') %]
[% NEXT IF field == "qa_contact" AND !Param('useqacontact') %]
[% NEXT IF field == "votes" AND !Param('usevotes') %]
<option value="[% field FILTER html %]"
[% " selected" IF default.$name.0 == field %]>
[% field_descs.$field || field FILTER html %]</option>
...
...
template/en/default/sidebar.xul.tmpl
View file @
120b63d5
...
...
@@ -105,9 +105,6 @@ function normal_keypress_handler( aEvent ) {
[% filtered_username = user.login FILTER url_quote %]
<text
class=
"text-link"
onclick=
"load_relative_url('[% Param('mybugstemplate').replace('%userid%', filtered_username) FILTER js FILTER html %]')"
value=
"my [% terms.bugs %]"
/>
[%- END %]
[%- IF Param('usevotes') %]
<text
class=
"text-link"
onclick=
"load_relative_url('votes.cgi?action=show_user')"
value=
"my votes"
/>
[%- END %]
[%- FOREACH q = user.queries %]
<text
class=
"text-link"
onclick=
"load_relative_url('buglist.cgi?cmdtype=runnamed&namedcmd=[% q.name FILTER url_quote %]')"
value=
"[% q.name FILTER html %]"
/>
...
...
votes.cgi
View file @
120b63d5
...
...
@@ -13,350 +13,36 @@
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# The Initial Developer of the Original Code is Netscape Communications
# Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All
# Rights Reserved.
# The Initial Developer of the Original Code is Everything Solved, Inc.
# Portions created by the Initial Developer are Copyright (C) 2010 the
# Initial Developer. All Rights Reserved.
#
# Contributor(s):
Terry Weissman <terry@mozilla.org>
#
Stephan Niemz <st.n@gmx.net
>
# Christopher Aillon <christopher@aillon.com>
#
Gervase Markham <gerv@gerv.net>
#
Frédéric Buclin <LpSolit@gmail.com>
# Contributor(s):
#
Max Kanat-Alexander <mkanat@bugzilla.org
>
#
This script remains as a backwards-compatibility URL for before
#
the time that Voting was an extension.
use
strict
;
use
lib
qw(. lib)
;
use
Bugzilla
;
use
Bugzilla::
Constants
;
use
Bugzilla::
Util
;
use
Bugzilla::
Error
;
use
Bugzilla::
Bug
;
use
Bugzilla::
User
;
use
Bugzilla::
Product
;
use
List::
Util
qw(min)
;
my
$cgi
=
Bugzilla
->
cgi
;
local
our
$vars
=
{};
# If the action is show_bug, you need a bug_id.
# If the action is show_user, you can supply a userid to show the votes for
# another user, otherwise you see your own.
# If the action is vote, your votes are set to those encoded in the URL as
# <bug_id>=<votes>.
#
# If no action is defined, we default to show_bug if a bug_id is given,
# otherwise to show_user.
my
$bug_id
=
$cgi
->
param
(
'bug_id'
);
my
$action
=
$cgi
->
param
(
'action'
)
||
(
$bug_id
?
"show_bug"
:
"show_user"
);
if
(
$action
eq
"show_bug"
||
(
$action
eq
"show_user"
&&
defined
$cgi
->
param
(
'user_id'
)))
{
Bugzilla
->
login
();
}
else
{
Bugzilla
->
login
(
LOGIN_REQUIRED
);
}
################################################################################
# Begin Data/Security Validation
################################################################################
# Make sure the bug ID is a positive integer representing an existing
# bug that the user is authorized to access.
if
(
defined
$bug_id
)
{
my
$bug
=
Bugzilla::
Bug
->
check
(
$bug_id
);
$bug_id
=
$bug
->
id
;
}
################################################################################
# End Data/Security Validation
################################################################################
my
$to_url
;
my
$action
=
$cgi
->
param
(
'action'
);
if
(
$action
eq
"show_bug"
)
{
show_bug
(
$bug_id
);
$cgi
->
delete
(
'action'
);
$cgi
->
param
(
'id'
,
'voting/bug.html'
);
}
elsif
(
$action
eq
"show_user"
)
{
show_user
(
$bug_id
);
}
elsif
(
$action
eq
"vote"
)
{
record_votes
()
if
Bugzilla
->
params
->
{
'usevotes'
};
show_user
(
$bug_id
);
elsif
(
$action
eq
"show_user"
or
$action
eq
'vote'
)
{
$cgi
->
delete
(
'action'
)
unless
$action
eq
'vote'
;
$cgi
->
param
(
'id'
,
'voting/user.html'
);
}
else
{
ThrowCodeError
(
"unknown_action"
,
{
action
=>
$action
});
}
print
$cgi
->
redirect
(
'page.cgi?'
.
$cgi
->
query_string
);
exit
;
# Display the names of all the people voting for this one bug.
sub
show_bug
{
my
(
$bug_id
)
=
@_
;
my
$cgi
=
Bugzilla
->
cgi
;
my
$dbh
=
Bugzilla
->
dbh
;
my
$template
=
Bugzilla
->
template
;
ThrowCodeError
(
"missing_bug_id"
)
unless
defined
$bug_id
;
$vars
->
{
'bug_id'
}
=
$bug_id
;
$vars
->
{
'users'
}
=
$dbh
->
selectall_arrayref
(
'SELECT profiles.login_name,
profiles.userid AS id,
votes.vote_count
FROM votes
INNER JOIN profiles
ON profiles.userid = votes.who
WHERE votes.bug_id = ?'
,
{
'Slice'
=>
{}},
$bug_id
);
print
$cgi
->
header
();
$template
->
process
(
"bug/votes/list-for-bug.html.tmpl"
,
$vars
)
||
ThrowTemplateError
(
$template
->
error
());
}
# Display all the votes for a particular user. If it's the user
# doing the viewing, give them the option to edit them too.
sub
show_user
{
my
(
$bug_id
)
=
@_
;
my
$cgi
=
Bugzilla
->
cgi
;
my
$dbh
=
Bugzilla
->
dbh
;
my
$user
=
Bugzilla
->
user
;
my
$template
=
Bugzilla
->
template
;
# If a bug_id is given, and we're editing, we'll add it to the votes list.
$bug_id
||=
""
;
my
$who_id
=
$cgi
->
param
(
'user_id'
)
||
$user
->
id
;
my
$who
=
Bugzilla::
User
->
check
({
id
=>
$who_id
});
my
$canedit
=
(
Bugzilla
->
params
->
{
'usevotes'
}
&&
$user
->
id
==
$who
->
id
)
?
1
:
0
;
$dbh
->
bz_start_transaction
();
if
(
$canedit
&&
$bug_id
)
{
# Make sure there is an entry for this bug
# in the vote table, just so that things display right.
my
$has_votes
=
$dbh
->
selectrow_array
(
'SELECT vote_count FROM votes
WHERE bug_id = ? AND who = ?'
,
undef
,
(
$bug_id
,
$who
->
id
));
if
(
!
$has_votes
)
{
$dbh
->
do
(
'INSERT INTO votes (who, bug_id, vote_count)
VALUES (?, ?, 0)'
,
undef
,
(
$who
->
id
,
$bug_id
));
}
}
my
@all_bug_ids
;
my
@products
;
my
$products
=
$user
->
get_selectable_products
;
# Read the votes data for this user for each product.
foreach
my
$product
(
@$products
)
{
next
unless
(
$product
->
votes_per_user
>
0
);
my
@bugs
;
my
@bug_ids
;
my
$total
=
0
;
my
$onevoteonly
=
0
;
my
$vote_list
=
$dbh
->
selectall_arrayref
(
'SELECT votes.bug_id, votes.vote_count,
bugs.short_desc
FROM votes
INNER JOIN bugs
ON votes.bug_id = bugs.bug_id
WHERE votes.who = ?
AND bugs.product_id = ?
ORDER BY votes.bug_id'
,
undef
,
(
$who
->
id
,
$product
->
id
));
foreach
(
@$vote_list
)
{
my
(
$id
,
$count
,
$summary
)
=
@$_
;
$total
+=
$count
;
# Next if user can't see this bug. So, the totals will be correct
# and they can see there are votes 'missing', but not on what bug
# they are. This seems a reasonable compromise; the alternative is
# to lie in the totals.
next
if
!
$user
->
can_see_bug
(
$id
);
push
(
@bugs
,
{
id
=>
$id
,
summary
=>
$summary
,
count
=>
$count
});
push
(
@bug_ids
,
$id
);
push
(
@all_bug_ids
,
$id
);
}
$onevoteonly
=
1
if
(
min
(
$product
->
votes_per_user
,
$product
->
max_votes_per_bug
)
==
1
);
# Only add the product for display if there are any bugs in it.
if
(
$#bugs
>
-
1
)
{
push
(
@products
,
{
name
=>
$product
->
name
,
bugs
=>
\
@bugs
,
bug_ids
=>
\
@bug_ids
,
onevoteonly
=>
$onevoteonly
,
total
=>
$total
,
maxvotes
=>
$product
->
votes_per_user
,
maxperbug
=>
$product
->
max_votes_per_bug
});
}
}
$dbh
->
do
(
'DELETE FROM votes WHERE vote_count <= 0'
);
$dbh
->
bz_commit_transaction
();
$vars
->
{
'canedit'
}
=
$canedit
;
$vars
->
{
'voting_user'
}
=
{
"login"
=>
$who
->
name
};
$vars
->
{
'products'
}
=
\
@products
;
$vars
->
{
'bug_id'
}
=
$bug_id
;
$vars
->
{
'all_bug_ids'
}
=
\
@all_bug_ids
;
print
$cgi
->
header
();
$template
->
process
(
"bug/votes/list-for-user.html.tmpl"
,
$vars
)
||
ThrowTemplateError
(
$template
->
error
());
}
# Update the user's votes in the database.
sub
record_votes
{
############################################################################
# Begin Data/Security Validation
############################################################################
my
$cgi
=
Bugzilla
->
cgi
;
my
$dbh
=
Bugzilla
->
dbh
;
my
$template
=
Bugzilla
->
template
;
# Build a list of bug IDs for which votes have been submitted. Votes
# are submitted in form fields in which the field names are the bug
# IDs and the field values are the number of votes.
my
@buglist
=
grep
{
/^[1-9][0-9]*$/
}
$cgi
->
param
();
# If no bugs are in the buglist, let's make sure the user gets notified
# that their votes will get nuked if they continue.
if
(
scalar
(
@buglist
)
==
0
)
{
if
(
!
defined
$cgi
->
param
(
'delete_all_votes'
))
{
print
$cgi
->
header
();
$template
->
process
(
"bug/votes/delete-all.html.tmpl"
,
$vars
)
||
ThrowTemplateError
(
$template
->
error
());
exit
();
}
elsif
(
$cgi
->
param
(
'delete_all_votes'
)
==
0
)
{
print
$cgi
->
redirect
(
"votes.cgi"
);
exit
();
}
}
# Call check() on each bug ID to make sure it is a positive
# integer representing an existing bug that the user is authorized
# to access, and make sure the number of votes submitted is also
# a non-negative integer (a series of digits not preceded by a
# minus sign).
my
%
votes
;
foreach
my
$id
(
@buglist
)
{
my
$bug
=
Bugzilla::
Bug
->
check
(
$id
);
$id
=
$bug
->
id
;
$votes
{
$id
}
=
$cgi
->
param
(
$id
);
detaint_natural
(
$votes
{
$id
})
||
ThrowUserError
(
"votes_must_be_nonnegative"
);
}
############################################################################
# End Data/Security Validation
############################################################################
my
$who
=
Bugzilla
->
user
->
id
;
# If the user is voting for bugs, make sure they aren't overstuffing
# the ballot box.
if
(
scalar
(
@buglist
))
{
my
%
prodcount
;
my
%
products
;
# XXX - We really need a $bug->product() method.
foreach
my
$bug_id
(
@buglist
)
{
my
$bug
=
new
Bugzilla::
Bug
(
$bug_id
);
my
$prod
=
$bug
->
product
;
$products
{
$prod
}
||=
new
Bugzilla::
Product
({
name
=>
$prod
});
$prodcount
{
$prod
}
||=
0
;
$prodcount
{
$prod
}
+=
$votes
{
$bug_id
};
# Make sure we haven't broken the votes-per-bug limit
(
$votes
{
$bug_id
}
<=
$products
{
$prod
}
->
max_votes_per_bug
)
||
ThrowUserError
(
"too_many_votes_for_bug"
,
{
max
=>
$products
{
$prod
}
->
max_votes_per_bug
,
product
=>
$prod
,
votes
=>
$votes
{
$bug_id
}});
}
# Make sure we haven't broken the votes-per-product limit
foreach
my
$prod
(
keys
(
%
prodcount
))
{
(
$prodcount
{
$prod
}
<=
$products
{
$prod
}
->
votes_per_user
)
||
ThrowUserError
(
"too_many_votes_for_product"
,
{
max
=>
$products
{
$prod
}
->
votes_per_user
,
product
=>
$prod
,
votes
=>
$prodcount
{
$prod
}});
}
}
# Update the user's votes in the database. If the user did not submit
# any votes, they may be using a form with checkboxes to remove all their
# votes (checkboxes are not submitted along with other form data when
# they are not checked, and Bugzilla uses them to represent single votes
# for products that only allow one vote per bug). In that case, we still
# need to clear the user's votes from the database.
my
%
affected
;
$dbh
->
bz_start_transaction
();
# Take note of, and delete the user's old votes from the database.
my
$bug_list
=
$dbh
->
selectcol_arrayref
(
'SELECT bug_id FROM votes
WHERE who = ?'
,
undef
,
$who
);
foreach
my
$id
(
@$bug_list
)
{
$affected
{
$id
}
=
1
;
}
$dbh
->
do
(
'DELETE FROM votes WHERE who = ?'
,
undef
,
$who
);
my
$sth_insertVotes
=
$dbh
->
prepare
(
'INSERT INTO votes (who, bug_id, vote_count)
VALUES (?, ?, ?)'
);
# Insert the new values in their place
foreach
my
$id
(
@buglist
)
{
if
(
$votes
{
$id
}
>
0
)
{
$sth_insertVotes
->
execute
(
$who
,
$id
,
$votes
{
$id
});
}
$affected
{
$id
}
=
1
;
}
# Update the cached values in the bugs table
print
$cgi
->
header
();
my
@updated_bugs
=
();
my
$sth_getVotes
=
$dbh
->
prepare
(
"SELECT SUM(vote_count) FROM votes
WHERE bug_id = ?"
);
my
$sth_updateVotes
=
$dbh
->
prepare
(
"UPDATE bugs SET votes = ?
WHERE bug_id = ?"
);
foreach
my
$id
(
keys
%
affected
)
{
$sth_getVotes
->
execute
(
$id
);
my
$v
=
$sth_getVotes
->
fetchrow_array
||
0
;
$sth_updateVotes
->
execute
(
$v
,
$id
);
my
$confirmed
=
CheckIfVotedConfirmed
(
$id
);
push
(
@updated_bugs
,
$id
)
if
$confirmed
;
}
$dbh
->
bz_commit_transaction
();
$vars
->
{
'type'
}
=
"votes"
;
$vars
->
{
'mailrecipients'
}
=
{
'changer'
=>
Bugzilla
->
user
->
login
};
$vars
->
{
'title_tag'
}
=
'change_votes'
;
foreach
my
$bug_id
(
@updated_bugs
)
{
$vars
->
{
'id'
}
=
$bug_id
;
$template
->
process
(
"bug/process/results.html.tmpl"
,
$vars
)
||
ThrowTemplateError
(
$template
->
error
());
# Set header_done to 1 only after the first bug.
$vars
->
{
'header_done'
}
=
1
;
}
$vars
->
{
'votes_recorded'
}
=
1
;
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment