Index: account_voting_page.php =================================================================== --- account_voting_page.php (revision 0) +++ account_voting_page.php (revision 0) @@ -0,0 +1,156 @@ +. + + # -------------------------------------------------------- + # $Id: $ + # -------------------------------------------------------- + +require_once( 'core.php' ); +$t_core_path = config_get( 'core_path' ); +require_once( $t_core_path.'current_user_api.php' ); +require_once( $t_core_path.'vote_api.php' ); +require_once( $t_core_path.'project_api.php' ); + +if ( current_user_is_anonymous() ) { + access_denied(); +} + +$t_current_user_id = auth_get_current_user_id(); +$t_resolved = config_get( 'bug_resolved_status_threshold' ); +$f_show_all = gpc_get_bool( 'show_all', false ); + +# start the page +html_page_top1( lang_get( 'my_votes' ) ); +html_page_top2(); + +$t_votes = vote_get_user_votes(); + +# get all information for issues ready for display to user +$t_votes_info = array(); +foreach($t_votes as $t_vote) +{ + $t_issue = bug_get($t_vote['issue_id']); + if ( ($t_issue->status < $t_resolved) || $f_show_all ) + { + $t_project_name = project_get_name($t_issue->project_id); + $t_votes_info[] = array('vote'=>$t_vote, 'issue'=>$t_issue, 'project_name'=>$t_project_name); + } +} + +?> + +
+ + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + 0){ + ?> + + + + + + + + + + + + + + + + + + + +
+ +
+ + + 0)?('+'.$t_vote_info['vote']['weight']):$t_vote_info['vote']['weight'] ?> + + votes_num_voters ?> + + votes_positive - $t_vote_info['issue']->votes_negative; + echo ($t_balance>0)?('+'.$t_balance):$t_balance; + ?> + + + + status ) ); ?> + + summary ); + if ( VS_PRIVATE == $t_vote_info['issue']->view_state ) { + printf( ' (%s)', $t_icon_path . 'protected.gif', lang_get( 'private' ), lang_get( 'private' ) ); + } + ?> +
+ = + + + +
+ +
+ ( $f_show_all ? 0 : 1 ) ) ); +?> +
+ + Index: admin/schema.php =================================================================== --- admin/schema.php (revision 5500) +++ admin/schema.php (working copy) @@ -404,7 +404,21 @@ " ) ); $upgrade[] = Array( 'AddColumnSQL', Array( db_get_table( 'mantis_project_version_table' ), " obsolete L NOTNULL DEFAULT \" '0' \"" ) ); + +# first version of voting +$upgrade[] = Array( 'CreateTableSQL', Array( db_get_table( 'mantis_bug_votes_table' ), " + issue_id I UNSIGNED NOTNULL PRIMARY DEFAULT '0', + user_id I UNSIGNED NOTNULL PRIMARY DEFAULT '0', + weight I NOTNULL DEFAULT '1' + ", Array( 'mysql' => 'TYPE=MyISAM', 'pgsql' => 'WITHOUT OIDS' ) ) ); $upgrade[] = Array( 'AddColumnSQL', Array( db_get_table( 'mantis_bug_table' ), " + votes_positive I UNSIGNED NOTNULL DEFAULT '0', + votes_negative I UNSIGNED NOTNULL DEFAULT '0', + votes_num_voters I UNSIGNED NOTNULL DEFAULT '0' + " ) ); +$upgrade[] = Array('CreateIndexSQL',Array('idx_votes_num_voters',db_get_table('mantis_bug_table'),'votes_num_voters')); + +$upgrade[] = Array( 'AddColumnSQL', Array( db_get_table( 'mantis_bug_table' ), " due_date T NOTNULL DEFAULT '" . db_null_date() . "' " ) ); $upgrade[] = Array( 'AddColumnSQL', Array( db_get_table( 'mantis_custom_field_table' ), " Index: bug_view_advanced_page.php =================================================================== --- bug_view_advanced_page.php (revision 5500) +++ bug_view_advanced_page.php (working copy) @@ -588,6 +588,9 @@ . + +# -------------------------------------------------------- +# $Id: $ +# -------------------------------------------------------- + +require_once( 'core.php' ); +$t_core_path = config_get( 'core_path' ); +require_once( $t_core_path.'current_user_api.php' ); +require_once( $t_core_path.'vote_api.php' ); + +if ( vote_is_enabled() ) +{ + $f_bug_id = gpc_get_int( 'bug_id' ); + $f_weight = gpc_get_int( 'vote_weight' ); + $t_user_id = auth_get_current_user_id(); + + access_ensure_bug_level( config_get( 'voting_place_vote_threshold' ), $f_bug_id, $t_user_id ); + + if (!vote_exists($f_bug_id, $t_user_id)){ + vote_add($f_bug_id, $f_weight, $t_user_id); + } + print_successful_redirect_to_bug($f_bug_id); +} +else +{ + trigger_error( ERROR_VOTING_NOT_ENABLED, ERROR ); +} + Index: bug_vote_delete.php =================================================================== --- bug_vote_delete.php (revision 0) +++ bug_vote_delete.php (revision 0) @@ -0,0 +1,44 @@ +. + +# -------------------------------------------------------- +# $Id: $ +# -------------------------------------------------------- + +require_once( 'core.php' ); +$t_core_path = config_get( 'core_path' ); +require_once( $t_core_path.'current_user_api.php' ); +require_once( $t_core_path.'vote_api.php' ); + +if ( vote_is_enabled() ) +{ + $f_bug_id = gpc_get_int( 'bug_id' ); + $t_user_id = auth_get_current_user_id(); + + access_ensure_bug_level( config_get( 'voting_place_vote_threshold' ), $f_bug_id, $t_user_id ); + + vote_delete($f_bug_id, $t_user_id); + + print_successful_redirect_to_bug($f_bug_id); +} +else +{ + trigger_error( ERROR_VOTING_NOT_ENABLED, ERROR ); +} + Index: bug_vote_list_view_inc.php =================================================================== --- bug_vote_list_view_inc.php (revision 0) +++ bug_vote_list_view_inc.php (revision 0) @@ -0,0 +1,200 @@ +. + + # -------------------------------------------------------- + # $Id: $ + # -------------------------------------------------------- + +# This include file prints out the list of users that have voted for the current +# bug. $f_bug_id must be set to the bug id +$t_core_path = config_get( 'core_path' ); +require_once( $t_core_path . 'vote_api.php' ); +require_once( $t_core_path . 'collapse_api.php' ); + + +$t_voting_enabled = vote_is_enabled(); +$t_current_user_id = auth_get_current_user_id(); + +# +# Determine whether the voting section should be shown. +# + +if ($t_voting_enabled) { + + $t_votes = vote_get_issue_votes( $f_bug_id ); + + $t_votes_exist = count( $t_votes ) > 0; + $t_can_view_vote_details = vote_can_view_vote_details($f_bug_id, $t_current_user_id); + $t_can_vote = vote_can_vote($f_bug_id, $t_current_user_id); + + $t_show_votes = $t_votes_exist || $t_can_vote; + + $t_total_positive = bug_get_field( $f_bug_id, 'votes_positive' ); + $t_total_negative = bug_get_field( $f_bug_id, 'votes_negative' ); + $t_total_votes = $t_total_positive - $t_total_negative; + + $t_total_voters = bug_get_field( $f_bug_id, 'votes_num_voters' ); + + $t_button_text = lang_get('vote_cast_button'); + $t_bug_id = string_attribute( $f_bug_id ); + + $t_voting_weight_options = config_get( 'voting_weight_options' ); + asort($t_voting_weight_options); + $t_voting_weight_default = config_get( 'voting_weight_default' ); + + $t_issue_project = bug_get_field( $f_bug_id, 'project_id'); + $t_max_votes = vote_max_votes( $t_current_user_id ); + $t_used_votes = vote_used_votes( $t_issue_project ); + $t_unlimited = (VOTES_UNLIMITED_VOTES == $t_max_votes); + + $t_available_votes = vote_available_votes( $t_issue_project, $t_current_user_id ); + $t_voting_max_vote_weight = vote_max_weight( $t_issue_project, $t_current_user_id ); + + $t_voting_per_project = config_get( 'voting_per_project' ); + +} else { + $t_show_votes = false; +} +?> + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
Vote on issue + $f_bug_id, 'action' => 'DELETE' ) ); + } + + } + else { # show 'add vote' button + ?> + +
+ 0 || $t_unlimited ){ ?> + + + + 0 ?> + + ( , + + ) + +
+ +
Summary + =
+ =
+ =
+ = + +
Voters List + +
+ =1)?'+'.$userVote['weight']:$userVote['weight'] ?> +
+ +
+ + + + + + + +
+ + ( = , = ) +
+ + Index: config_defaults_inc.php =================================================================== --- config_defaults_inc.php (revision 5500) +++ config_defaults_inc.php (working copy) @@ -550,22 +550,22 @@ # resolution, fixed_in_version, view_state, os, os_build, build (for product build), platform, version, date_submitted, attachment, # category, sponsorship_total, severity, status, last_updated, summary, bugnotes_count, description, # steps_to_reproduce, additional_information - $g_view_issues_page_columns = array ( 'selection', 'edit', 'priority', 'id', 'sponsorship_total', 'bugnotes_count', 'attachment', 'category', 'severity', 'status', 'last_updated', 'summary' ); + $g_view_issues_page_columns = array ( 'selection', 'edit', 'priority', 'id', 'votes_total', 'votes_num_voters', 'sponsorship_total', 'bugnotes_count', 'attachment', 'category', 'severity', 'status', 'last_updated', 'summary' ); # The default columns to be included in the Print Issues Page. # This can be overriden using Manage -> Manage Configuration -> Manage Columns # Also each user can configure their own columns using My Account -> Manage Columns - $g_print_issues_page_columns = array ( 'selection', 'priority', 'id', 'sponsorship_total', 'bugnotes_count', 'attachment', 'category', 'severity', 'status', 'last_updated', 'summary' ); + $g_print_issues_page_columns = array ( 'selection', 'priority', 'id', 'votes_total', 'votes_num_voters', 'sponsorship_total', 'bugnotes_count', 'attachment', 'category', 'severity', 'status', 'last_updated', 'summary' ); # The default columns to be included in the CSV export. # This can be overriden using Manage -> Manage Configuration -> Manage Columns # Also each user can configure their own columns using My Account -> Manage Columns - $g_csv_columns = array ( 'id', 'project_id', 'reporter_id', 'handler_id', 'priority', 'severity', 'reproducibility', 'version', 'projection', 'category', 'date_submitted', 'eta', 'os', 'os_build', 'platform', 'view_state', 'last_updated', 'summary', 'status', 'resolution', 'fixed_in_version' ); + $g_csv_columns = array ( 'id', 'project_id', 'reporter_id', 'handler_id', 'priority', 'severity', 'reproducibility', 'version', 'projection', 'category', 'date_submitted', 'eta', 'os', 'os_build', 'platform', 'view_state', 'last_updated', 'summary', 'status', 'resolution', 'fixed_in_version', 'votes_positive', 'votes_negative', 'votes_num_voters' ); # The default columns to be included in the Excel export. # This can be overriden using Manage -> Manage Configuration -> Manage Columns # Also each user can configure their own columns using My Account -> Manage Columns - $g_excel_columns = array ( 'id', 'project_id', 'reporter_id', 'handler_id', 'priority', 'severity', 'reproducibility', 'version', 'projection', 'category', 'date_submitted', 'eta', 'os', 'os_build', 'platform', 'view_state', 'last_updated', 'summary', 'status', 'resolution', 'fixed_in_version' ); + $g_excel_columns = array ( 'id', 'project_id', 'reporter_id', 'handler_id', 'priority', 'severity', 'reproducibility', 'version', 'projection', 'category', 'date_submitted', 'eta', 'os', 'os_build', 'platform', 'view_state', 'last_updated', 'summary', 'status', 'resolution', 'fixed_in_version', 'votes_positive', 'votes_negative', 'votes_num_voters' ); # --- show projects when in All Projects mode --- $g_show_bug_project_links = ON; @@ -1443,6 +1443,7 @@ $g_db_table['mantis_config_table'] = '%db_table_prefix%_config%db_table_suffix%'; $g_db_table['mantis_database_table'] = '%db_table_prefix%_database%db_table_suffix%'; $g_db_table['mantis_email_table'] = '%db_table_prefix%_email%db_table_suffix%'; + $g_db_table['mantis_bug_votes_table'] = '%db_table_prefix%_bug_votes%db_table_suffix%'; ########################### # Mantis Enum Strings @@ -2012,6 +2013,36 @@ # management threshold. $g_manage_plugin_threshold = ADMINISTRATOR; + + ############################# + # Voting System + ############################# + + # enable or disable the whole voting feature. + $g_voting_enabled = ON; + + # access level required for users to vote on issues. + $g_voting_place_vote_threshold = REPORTER; + + # access level required for users to view the users who voted and their votes. + $g_voting_view_user_votes_threshold = DEVELOPER; + + # default number of votes allowed per user + $g_voting_default_num_votes = 10; # votes can be set for all user levels as an integer ( set to VOTES_UNLIMITED_VOTES to get unlimited votes ) + $g_voting_default_num_votes = array( DEVELOPER => 25 , REPORTER => 10 ); # or you can set votes by user type, if a level is not specified then it will use the next lowest level available + + # default voting weights and thier labels, value needs to be integer, while key is a string eg: '+10 (Highly desired)' + $g_voting_weight_options = array('+1'=>1, '+2'=>2, '+5'=>5, '+10'=>10, '-1'=>-1, '-2'=>-2, '-5'=>-5, '-10'=>-10); + + # the maximum weight a user at a given level may use in a single vote + $g_voting_max_vote_weight = 5; #max vote weight can be an integer + $g_voting_max_vote_weight = array( DEVELOPER => 10 , REPORTER => 5 ); # or set by user type, eg: even though a reporter may have 10 votes, they may only use up to weight 5 in a single vote + + # voting weight that should be initially selected when casting a vote, usually the minimum positive vote + $g_voting_weight_default = 1; + + # whether you get your votes counted per project or globally, if ON then you will get $g_voting_default_num_votes per project, if it is OFF your votes are spread across all projects + $g_voting_per_project = ON; ############################# # Due Date Index: config_filter_defaults_inc.php =================================================================== --- config_filter_defaults_inc.php (revision 5500) +++ config_filter_defaults_inc.php (working copy) @@ -37,7 +37,7 @@ define( 'FILTER_PROPERTY_RESOLUTION_ID', 'show_resolution' ); define( 'FILTER_PROPERTY_PRODUCT_BUILD', 'show_build' ); define( 'FILTER_PROPERTY_PRODUCT_VERSION', 'show_version' ); - + define( 'FILTER_PROPERTY_VOTES_USER_ID', 'user_votes' ); define( 'FILTER_PROPERTY_MONITOR_USER_ID', 'user_monitor' ); define( 'FILTER_PROPERTY_HIDE_STATUS_ID', 'hide_status' ); define( 'FILTER_PROPERTY_SORT_FIELD_NAME', 'sort' ); @@ -90,6 +90,7 @@ define( 'FILTER_SEARCH_PLATFORM', 'platform' ); define( 'FILTER_SEARCH_OS', 'os' ); define( 'FILTER_SEARCH_OS_BUILD', 'os_build' ); + define( 'FILTER_SEARCH_VOTES_USER_ID', 'votes_user_id' ); define( 'FILTER_SEARCH_MONITOR_USER_ID', 'monitor_user_id' ); define( 'FILTER_SEARCH_PRODUCT_BUILD', 'product_build' ); define( 'FILTER_SEARCH_PRODUCT_VERSION', 'product_version' ); Index: core/bug_api.php =================================================================== --- core/bug_api.php (revision 5500) +++ core/bug_api.php (working copy) @@ -35,6 +35,9 @@ require_once( $t_core_dir . 'sponsorship_api.php' ); require_once( $t_core_dir . 'twitter_api.php' ); require_once( $t_core_dir . 'tag_api.php' ); + require_once( $t_core_dir . 'vote_api.php' ); + + # MASC RELATIONSHIP require_once( $t_core_dir.'relationship_api.php' ); ### Bug API ### @@ -68,6 +71,9 @@ var $summary = ''; var $sponsorship_total = 0; var $sticky = 0; + var $votes_positive = 0; + var $votes_negative = 0; + var $votes_num_voters = 0; # omitted: # var $bug_text_id @@ -856,6 +862,9 @@ # Delete all sponsorships sponsorship_delete( sponsorship_get_all_ids( $p_bug_id ) ); + + # Delete all votes on this bug + vote_delete_issue_votes( $p_bug_id ); # MASC RELATIONSHIP # we delete relationships even if the feature is currently off. @@ -1011,12 +1020,18 @@ view_state=" . db_param() .", summary=" . db_param() .", sponsorship_total=" . db_param() .", + votes_num_voters=" . db_param() .", + votes_positive=" . db_param() .", + votes_negative=" . db_param() .", sticky=" . db_param() .", due_date=" . db_param() ." WHERE id=" . db_param(); $t_fields[] = $c_bug_data->view_state; $t_fields[] = $c_bug_data->summary; $t_fields[] = $c_bug_data->sponsorship_total; + $t_fields[] = $c_bug_data->votes_num_voters; + $t_fields[] = $c_bug_data->votes_positive; + $t_fields[] = $c_bug_data->votes_negative; $t_fields[] = (bool)$c_bug_data->sticky; $t_fields[] = $c_due_date; $t_fields[] = $c_bug_id; @@ -1049,6 +1064,11 @@ history_log_event_direct( $p_bug_id, 'view_state', $t_old_data->view_state, $p_bug_data->view_state ); history_log_event_direct( $p_bug_id, 'summary', $t_old_data->summary, $p_bug_data->summary ); history_log_event_direct( $p_bug_id, 'sponsorship_total', $t_old_data->sponsorship_total, $p_bug_data->sponsorship_total ); + # @REVIEW should these voting attributes show up in the history? + #history_log_event_direct( $p_bug_id, 'votes_num_voters', $t_old_data->votes_num_voters, $p_bug_data->votes_num_voters ); + #history_log_event_direct( $p_bug_id, 'votes_positive', $t_old_data->votes_positive, $p_bug_data->votes_positive ); + #history_log_event_direct( $p_bug_id, 'votes_negative', $t_old_data->votes_negative, $p_bug_data->votes_negative ); + history_log_event_direct( $p_bug_id, 'sticky', $t_old_data->sticky, $p_bug_data->sticky ); history_log_event_direct( $p_bug_id, 'due_date', ( $t_old_data->due_date != db_unixtimestamp( db_null_date() ) ) ? $t_old_data->due_date : null, @@ -1395,6 +1415,9 @@ case 'view_state': case 'profile_id': case 'sponsorship_total': + case 'votes_positive': + case 'votes_negative': + case 'votes_num_voters': $c_value = (int)$p_value; break; Index: core/columns_api.php =================================================================== --- core/columns_api.php (revision 5500) +++ core/columns_api.php (working copy) @@ -56,6 +56,9 @@ 'selection', 'severity', 'sponsorship_total', + 'votes_positive', + 'votes_negative', + 'votes_num_voters', 'status', 'steps_to_reproduce', // new 'summary', @@ -834,4 +837,31 @@ echo ''; } + + + function print_column_title_votes_total( $p_sort, $p_dir, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) { + echo ''; + print_view_bug_sort_link( lang_get( 'vote_balance' ), 'votes_total', $p_sort, $p_dir, $p_columns_target ); + print_sort_icon( $p_dir, $p_sort, 'votes_total' ); + echo ''; + } + + function print_column_title_votes_num_voters( $p_sort, $p_dir, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) { + echo ''; + print_view_bug_sort_link( lang_get( 'vote_num_voters' ), 'votes_num_voters', $p_sort, $p_dir, $p_columns_target ); + print_sort_icon( $p_dir, $p_sort, 'votes_num_voters' ); + echo ''; + } + + function print_column_votes_total( $p_row, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) { + echo ''; + echo (($p_row['votes_total']>0)?'+':'') . $p_row['votes_total']; + echo ''; + } + + function print_column_votes_num_voters( $p_row, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) { + echo ''; + echo $p_row['votes_num_voters']; + echo ''; + } ?> Index: core/constant_inc.php =================================================================== --- core/constant_inc.php (revision 5500) +++ core/constant_inc.php (working copy) @@ -170,6 +170,8 @@ define( 'TAG_ATTACHED', 25 ); define( 'TAG_DETACHED', 26 ); define( 'TAG_RENAMED', 27 ); + define( 'BUGVOTE_ADDED', 28 ); + define( 'BUGVOTE_DELETED', 29 ); # bug relationship constants define( 'BUG_DUPLICATE', 0 ); @@ -343,6 +345,13 @@ # ERROR_FORM_* define ( 'ERROR_FORM_TOKEN_INVALID', 2800 ); + + #ERROR_VOTING_* + define( 'ERROR_VOTING_NOT_ENABLED', 2900 ); + define( 'ERROR_VOTING_OVER_LIMIT', 2901 ); + + # voting + define('VOTES_UNLIMITED_VOTES', -1); # Status Legend Position define( 'STATUS_LEGEND_POSITION_TOP', 1); Index: core/csv_api.php =================================================================== --- core/csv_api.php (revision 5500) +++ core/csv_api.php (working copy) @@ -254,4 +254,16 @@ function csv_format_selection( $p_duplicate_id ) { return csv_escape_string( '' ); } + + function csv_format_votes_positive( $p_votes_positive ) { + return csv_escape_string( $p_votes_positive ); + } + + function csv_format_votes_negative( $p_votes_negative ) { + return csv_escape_string( $p_votes_negative ); + } + + function csv_format_votes_num_voters( $p_votes_num_voters ) { + return csv_escape_string( $p_votes_num_voters ); + } ?> Index: core/excel_api.php =================================================================== --- core/excel_api.php (revision 5500) +++ core/excel_api.php (working copy) @@ -428,4 +428,14 @@ // field is not linked to project return excel_prepare_string( '' ); } + + function excel_format_votes_positive( $p_votes_positive ) { + return excel_prepare_string( $p_votes_positive ); + } + function excel_format_votes_negative( $p_votes_negative ) { + return excel_prepare_string( $p_votes_negative ); + } + function excel_format_votes_num_voters( $p_votes_num_voters ) { + return excel_prepare_string( $p_votes_num_voters ); + } ?> Index: core/filter_api.php =================================================================== --- core/filter_api.php (revision 5500) +++ core/filter_api.php (working copy) @@ -74,10 +74,18 @@ if ( !filter_field_is_any( $p_custom_filter[FILTER_PROPERTY_STATUS_ID] ) ) { $t_query[] = filter_encode_field_and_value( FILTER_SEARCH_STATUS_ID, $p_custom_filter[FILTER_PROPERTY_STATUS_ID] ); } + + if ( !filter_field_is_any( $p_custom_filter[FILTER_PROPERTY_VOTES_USER_ID] ) ) { + $t_query[] = filter_encode_field_and_value( FILTER_SEARCH_VOTES_USER_ID, $p_custom_filter[FILTER_PROPERTY_VOTES_USER_ID] ); + } if ( !filter_field_is_any( $p_custom_filter[FILTER_PROPERTY_MONITOR_USER_ID] ) ) { $t_query[] = filter_encode_field_and_value( FILTER_SEARCH_MONITOR_USER_ID, $p_custom_filter[FILTER_PROPERTY_MONITOR_USER_ID] ); } + + if ( !filter_field_is_any( $p_custom_filter[FILTER_PROPERTY_VOTES_USER_ID] ) ) { + $t_query[] = filter_encode_field_and_value( FILTER_PROPERTY_VOTES_USER_ID, $p_custom_filter[FILTER_PROPERTY_VOTES_USER_ID] ); + } if ( !filter_field_is_any( $p_custom_filter[FILTER_PROPERTY_HANDLER_ID] ) ) { $t_query[] = filter_encode_field_and_value( FILTER_SEARCH_HANDLER_ID, $p_custom_filter[FILTER_PROPERTY_HANDLER_ID] ); @@ -545,6 +553,7 @@ FILTER_PROPERTY_FIXED_IN_VERSION => 'string', FILTER_PROPERTY_TARGET_VERSION => 'string', FILTER_PROPERTY_MONITOR_USER_ID => 'int', + FILTER_PROPERTY_VOTES_USER_ID => 'int', 'show_profile' => 'int' ); foreach( $t_multi_select_list as $t_multi_field_name => $t_multi_field_type ) { @@ -628,6 +637,7 @@ FILTER_PROPERTY_PRODUCT_VERSION => Array ( '0' => META_FILTER_ANY ), FILTER_PROPERTY_HIDE_STATUS_ID => Array ( '0' => $t_hide_status_default ), FILTER_PROPERTY_MONITOR_USER_ID => Array ( '0' => META_FILTER_ANY ), + FILTER_PROPERTY_VOTES_USER_ID => Array ( '0' => META_FILTER_ANY ), FILTER_PROPERTY_SORT_FIELD_NAME => 'last_updated', FILTER_PROPERTY_SORT_DIRECTION => 'DESC', FILTER_PROPERTY_ISSUES_PER_PAGE => config_get( 'default_limit_view' ) @@ -859,6 +869,7 @@ $t_bugnote_text_table = db_get_table( 'mantis_bugnote_text_table' ); $t_project_table = db_get_table( 'mantis_project_table' ); $t_bug_monitor_table = db_get_table( 'mantis_bug_monitor_table' ); + $t_bug_votes_table = db_get_table( 'mantis_bug_votes_table' ); $t_limit_reporters = config_get( 'limit_reporters' ); $t_bug_relationship_table = db_get_table( 'mantis_bug_relationship_table' ); $t_report_bug_threshold = config_get( 'report_bug_threshold' ); @@ -1518,6 +1529,35 @@ } } + # users voting on an issue + $t_select_clauses[] = '(votes_positive - votes_negative) as votes_total'; # @REVIEW is this the correct mantis way to be doing this? votes_total is a derived column + if ( !filter_field_is_any( $t_filter[ FILTER_PROPERTY_VOTES_USER_ID ] ) ) { + $t_clauses = array(); + $t_table_name = 'user_votes'; + array_push( $t_from_clauses, $t_bug_votes_table ); + array_push( $t_join_clauses, "LEFT JOIN $t_bug_votes_table $t_table_name ON $t_table_name.issue_id = $t_bug_table.id" ); + + foreach( $t_filter[FILTER_PROPERTY_VOTES_USER_ID] as $t_filter_member ) { + $c_user_monitor = db_prepare_int( $t_filter_member ); + if ( META_FILTER_MYSELF == $c_user_monitor ) { + array_push( $t_clauses, $c_user_id ); + } else { + array_push( $t_clauses, $c_user_monitor ); + } + } + if ( 1 < count( $t_clauses ) ) { + foreach( $t_clauses as $t_clause ) { + $t_where_tmp[] = db_param($t_where_param_count++); + $t_where_params[] = $t_clause; + } + array_push( $t_where_clauses, "( $t_table_name.user_id in (". implode( ', ', $t_where_tmp ) .") )" ); + } else { + $t_where_params[] = $t_clauses[0]; + array_push( $t_where_clauses, "( $t_table_name.user_id=" . db_param($t_where_param_count++). " )" ); + } + } + + # bug relationship $t_any_found = false; $c_rel_type = $t_filter[ FILTER_PROPERTY_RELATIONSHIP_TYPE ]; @@ -2637,9 +2677,12 @@ : - + : + + : + 8 ) { echo ' '; } ?> @@ -2660,7 +2703,7 @@ print_multivalue_field( FILTER_PROPERTY_OS_BUILD, $t_filter[ FILTER_PROPERTY_OS_BUILD ] ); ?> - + '; ?> + + + + '; + } else { + $t_first_flag = false; + } + $t_output = $t_output . $t_this_name; + } + if ( true == $t_any_found ) { + PRINT lang_get( 'any' ); + } else { + PRINT $t_output; + } + } + ?> + + + + ' . $p_new_value; break; + case BUGVOTE_ADDED: + $t_note = lang_get( 'bugvote_added' ) . ": " . $p_old_value; + break; + case BUGVOTE_DELETED: + $t_note = lang_get( 'bugvote_deleted' ) . ": " . $p_old_value; + break; } } Index: core/html_api.php =================================================================== --- core/html_api.php (revision 5500) +++ core/html_api.php (working copy) @@ -69,6 +69,7 @@ require_once( $t_core_dir . 'user_api.php' ); require_once( $t_core_dir . 'rss_api.php' ); require_once( $t_core_dir . 'wiki_api.php' ); + require_once( $t_core_dir . 'vote_api.php' ); $g_rss_feed_url = null; @@ -853,6 +854,7 @@ $t_account_prefs_page = 'account_prefs_page.php'; $t_account_profile_menu_page = 'account_prof_menu_page.php'; $t_account_sponsor_page = 'account_sponsor_page.php'; + $t_account_voting_page = 'account_voting_page.php'; $t_account_manage_columns_page = 'account_manage_columns_page.php'; switch ( $p_page ) { @@ -868,6 +870,9 @@ case $t_account_sponsor_page: $t_account_sponsor_page = ''; break; + case $t_account_voting_page: + $t_account_voting_page = ''; + break; case $t_account_manage_columns_page: $t_account_manage_columns_page = ''; break; @@ -886,6 +891,13 @@ !current_user_is_anonymous() ) { print_bracket_link( helper_mantis_url( $t_account_sponsor_page ), lang_get( 'my_sponsorship' ) ); } + + if ( ( config_get( 'voting_enabled' ) == ON ) && + ( access_has_project_level( config_get( 'voting_place_vote_threshold' ) ) ) && + !current_user_is_anonymous() ) { + print_bracket_link( helper_mantis_url( $t_account_voting_page ), lang_get( 'my_votes' ) ); + } + } # -------------------- @@ -1288,7 +1300,7 @@ echo ''; html_button_bug_change_status( $p_bug_id ); echo ''; - + # MONITOR/UNMONITOR button echo ''; if ( !current_user_is_anonymous() ) { Index: core/my_view_inc.php =================================================================== --- core/my_view_inc.php (revision 5500) +++ core/my_view_inc.php (working copy) @@ -59,7 +59,8 @@ FILTER_PROPERTY_PRODUCT_BUILD => Array ( '0' => META_FILTER_ANY ), FILTER_PROPERTY_PRODUCT_VERSION => Array ( '0' => META_FILTER_ANY ), FILTER_PROPERTY_HIDE_STATUS_ID => Array ( '0' => $t_bug_resolved_status_threshold ), - FILTER_PROPERTY_MONITOR_USER_ID => Array ( '0' => META_FILTER_ANY ) + FILTER_PROPERTY_MONITOR_USER_ID => Array ( '0' => META_FILTER_ANY ), + 'user_votes' => Array ( '0' => META_FILTER_ANY ) ); $url_link_parameters['assigned'] = FILTER_PROPERTY_HANDLER_ID . '=' . $t_current_user_id . '&' . FILTER_PROPERTY_HIDE_STATUS_ID . '=' . $t_bug_resolved_status_threshold; @@ -74,7 +75,8 @@ FILTER_PROPERTY_PRODUCT_BUILD => Array ( '0' => META_FILTER_ANY ), FILTER_PROPERTY_PRODUCT_VERSION => Array ( '0' => META_FILTER_ANY ), FILTER_PROPERTY_HIDE_STATUS_ID => Array ( '0' => META_FILTER_NONE ), - FILTER_PROPERTY_MONITOR_USER_ID => Array ( '0' => META_FILTER_ANY ) + FILTER_PROPERTY_MONITOR_USER_ID => Array ( '0' => META_FILTER_ANY ), + 'user_votes' => Array ( '0' => META_FILTER_ANY ) ); $url_link_parameters['recent_mod'] = FILTER_PROPERTY_HIDE_STATUS_ID . '=none'; @@ -90,7 +92,8 @@ FILTER_PROPERTY_PRODUCT_BUILD => Array ( '0' => META_FILTER_ANY ), FILTER_PROPERTY_PRODUCT_VERSION => Array ( '0' => META_FILTER_ANY ), FILTER_PROPERTY_HIDE_STATUS_ID => Array ( '0' => $t_hide_status_default ), - FILTER_PROPERTY_MONITOR_USER_ID => Array ( '0' => META_FILTER_ANY ) + FILTER_PROPERTY_MONITOR_USER_ID => Array ( '0' => META_FILTER_ANY ), + 'user_votes' => Array ( '0' => META_FILTER_ANY ) ); $url_link_parameters['reported'] = FILTER_PROPERTY_REPORTER_ID . '=' . $t_current_user_id . '&' . FILTER_PROPERTY_HIDE_STATUS_ID . '=' . $t_hide_status_default; @@ -105,7 +108,8 @@ FILTER_PROPERTY_PRODUCT_BUILD => Array ( '0' => META_FILTER_ANY ), FILTER_PROPERTY_PRODUCT_VERSION => Array ( '0' => META_FILTER_ANY ), FILTER_PROPERTY_HIDE_STATUS_ID => Array ( '0' => $t_hide_status_default ), - FILTER_PROPERTY_MONITOR_USER_ID => Array ( '0' => META_FILTER_ANY ) + FILTER_PROPERTY_MONITOR_USER_ID => Array ( '0' => META_FILTER_ANY ), + 'user_votes' => Array ( '0' => META_FILTER_ANY ) ); $url_link_parameters['resolved'] = FILTER_PROPERTY_STATUS_ID . '=' . $t_bug_resolved_status_threshold . '&' . FILTER_PROPERTY_HIDE_STATUS_ID . '=' . $t_bug_resolved_status_threshold; @@ -120,7 +124,8 @@ FILTER_PROPERTY_PRODUCT_BUILD => Array ( '0' => META_FILTER_ANY ), FILTER_PROPERTY_PRODUCT_VERSION => Array ( '0' => META_FILTER_ANY ), FILTER_PROPERTY_HIDE_STATUS_ID => Array ( '0' => $t_hide_status_default ), - FILTER_PROPERTY_MONITOR_USER_ID => Array ( '0' => META_FILTER_ANY ) + FILTER_PROPERTY_MONITOR_USER_ID => Array ( '0' => META_FILTER_ANY ), + 'user_votes' => Array ( '0' => META_FILTER_ANY ) ); $url_link_parameters['unassigned'] = FILTER_PROPERTY_HANDLER_ID . '=[none]' . '&' . FILTER_PROPERTY_HIDE_STATUS_ID . '=' . $t_hide_status_default; #TODO: check. handler value looks wrong @@ -135,7 +140,8 @@ FILTER_PROPERTY_PRODUCT_BUILD => Array ( '0' => META_FILTER_ANY ), FILTER_PROPERTY_PRODUCT_VERSION => Array ( '0' => META_FILTER_ANY ), FILTER_PROPERTY_HIDE_STATUS_ID => Array ( '0' => $t_hide_status_default ), - FILTER_PROPERTY_MONITOR_USER_ID => Array ( '0' => $t_current_user_id ) + FILTER_PROPERTY_MONITOR_USER_ID => Array ( '0' => $t_current_user_id ), + 'user_votes' => Array ( '0' => META_FILTER_ANY ) ); $url_link_parameters['monitored'] = FILTER_PROPERTY_MONITOR_USER_ID . '=' . $t_current_user_id . '&' . FILTER_PROPERTY_HIDE_STATUS_ID . '=' . $t_hide_status_default; @@ -151,7 +157,8 @@ FILTER_PROPERTY_PRODUCT_BUILD => Array ( '0' => META_FILTER_ANY ), FILTER_PROPERTY_PRODUCT_VERSION => Array ( '0' => META_FILTER_ANY ), FILTER_PROPERTY_HIDE_STATUS_ID => Array ( '0' => $t_hide_status_default ), - FILTER_PROPERTY_MONITOR_USER_ID => Array ( '0' => META_FILTER_ANY ) + FILTER_PROPERTY_MONITOR_USER_ID => Array ( '0' => META_FILTER_ANY ), + 'user_votes' => Array ( '0' => META_FILTER_ANY ) ); $url_link_parameters['feedback'] = FILTER_PROPERTY_REPORTER_ID . '=' . $t_current_user_id . '&' . FILTER_PROPERTY_STATUS_ID . '=' . FEEDBACK . '&' . FILTER_PROPERTY_HIDE_STATUS_ID . '=' . $t_hide_status_default; @@ -166,7 +173,8 @@ FILTER_PROPERTY_PRODUCT_BUILD => Array ( '0' => META_FILTER_ANY ), FILTER_PROPERTY_PRODUCT_VERSION => Array ( '0' => META_FILTER_ANY ), FILTER_PROPERTY_HIDE_STATUS_ID => Array ( '0' => $t_hide_status_default ), - FILTER_PROPERTY_MONITOR_USER_ID => Array ( '0' => META_FILTER_ANY ) + FILTER_PROPERTY_MONITOR_USER_ID => Array ( '0' => META_FILTER_ANY ), + 'user_votes' => Array ( '0' => META_FILTER_ANY ) ); $url_link_parameters['verify'] = FILTER_PROPERTY_REPORTER_ID . '=' . $t_current_user_id . '&' . FILTER_PROPERTY_STATUS_ID . '=' . $t_bug_resolved_status_threshold; Index: core/print_api.php =================================================================== --- core/print_api.php (revision 5500) +++ core/print_api.php (working copy) @@ -104,7 +104,6 @@ # call print_successful_redirect() with that URL function print_successful_redirect_to_bug( $p_bug_id ) { $t_url = string_get_bug_view_url( $p_bug_id, auth_get_current_user_id() ); - print_successful_redirect( $t_url ); } Index: core/user_api.php =================================================================== --- core/user_api.php (revision 5500) +++ core/user_api.php (working copy) @@ -30,6 +30,7 @@ require_once( $t_core_dir . 'email_api.php' ); require_once( $t_core_dir . 'ldap_api.php' ); + require_once( $t_core_dir . 'vote_api.php' ); #=================================== @@ -560,6 +561,9 @@ $t_user_table = db_get_table('mantis_user_table'); user_ensure_unprotected( $p_user_id ); + + # Remove any votes the user has made on issues + vote_delete_user_votes( $p_user_id ); # Remove associated profiles user_delete_profiles( $p_user_id ); @@ -1086,7 +1090,6 @@ } $t_filter = unserialize( $t_cookie_detail[1] ); - $t_filter = filter_ensure_valid_filter( $t_filter ); return $t_filter; Index: core/vote_api.php =================================================================== --- core/vote_api.php (revision 0) +++ core/vote_api.php (revision 0) @@ -0,0 +1,471 @@ +. + + # -------------------------------------------------------- + # $Id: $ + # -------------------------------------------------------- + +$t_core_dir = dirname( __FILE__ ).DIRECTORY_SEPARATOR; +require_once( $t_core_dir . 'current_user_api.php' ); +require_once( $t_core_dir . 'history_api.php' ); +require_once( $t_core_dir . 'bug_api.php' ); +require_once( $t_core_dir . 'user_api.php' ); + + +/** + * vote_add + * + * @param integer $p_issue_id issue primary key + * @param integer $p_weight impact of vote + * @param integer $p_user_id user primary key + */ +function vote_add( $p_issue_id, $p_weight, $p_user_id = null ) +{ + if ( $p_issue_id < 1 ) + { + error_parameters( $p_issue_id ); + trigger_error( ERROR_BUG_NOT_FOUND , ERROR ); + } + + $t_issue_project = bug_get_field($p_issue_id, 'project_id'); + $t_vote_max_weight = vote_max_weight( $t_issue_project, $p_user_id ); + $t_unlimited = (VOTES_UNLIMITED_VOTES == $t_vote_max_weight); + + if ( ( $p_weight > $t_vote_max_weight && !$t_unlimited ) || $p_weight == 0 ) + { + trigger_error( ERROR_VOTING_OVER_LIMIT, ERROR ); + } + + $t_mantis_bug_votes_table = db_get_table( 'mantis_bug_votes_table' ); + + $query = "INSERT INTO $t_mantis_bug_votes_table + ( issue_id, user_id, weight ) + VALUES + ( " . db_param(0) . ", " . db_param(1) . ", " . db_param(2) . " )"; + db_query_bound( $query, Array( (int)$p_issue_id, (int)$p_user_id, (int)$p_weight ) ); + + #update issue counters to keep in sync + vote_updates_counters_for_issue( $p_issue_id ); + + # log vote history + history_log_event_special( $p_issue_id, BUGVOTE_ADDED ); + bug_update_date($p_issue_id); +} + +/** + * vote_delete + * + * @param integer $p_issue_id + * @param integer $p_user_id + */ +function vote_delete( $p_issue_id, $p_user_id ) +{ + if ( $p_issue_id < 1 ) + { + error_parameters( $p_issue_id ); + trigger_error( ERROR_BUG_NOT_FOUND , ERROR ); + } + if ( $p_user_id < 1 ) + { + error_parameters( $p_user_id ); + trigger_error( ERROR_USER_NOT_FOUND , ERROR ); + } + + $t_mantis_bug_votes_table = db_get_table( 'mantis_bug_votes_table' ); + + # now remove vote from voting table + $query = "DELETE FROM $t_mantis_bug_votes_table WHERE issue_id = " . db_param(0) . " AND user_id = " . db_param(1); + db_query_bound($query, Array( (int)$p_issue_id, (int)$p_user_id )); + + #update issue counters to keep bug table in sync + vote_updates_counters_for_issue( $p_issue_id ); + + # log vote history + history_log_event_special( $p_issue_id, BUGVOTE_DELETED ); + bug_update_date($p_issue_id); +} + +/** + * vote_delete_issue_votes + * Deleting an issue should delete all associated votes. + * This should only be called post issue delete + * + * @param integer $p_issue_id issue primary key + */ +function vote_delete_issue_votes( $p_issue_id ) +{ + if ( $p_issue_id < 1 ) + { + error_parameters( $p_issue_id ); + trigger_error( ERROR_BUG_NOT_FOUND , ERROR ); + } + + $t_mantis_bug_votes_table = db_get_table( 'mantis_bug_votes_table' ); + $query = "DELETE FROM $t_mantis_bug_votes_table WHERE issue_id = " . db_param(0); + db_query_bound($query, Array( (int)$p_issue_id )); +} + +/** + * vote_delete_user_votes + * Deleting a user should delete all associated votes. + * + * @param integer $p_user_id user primary key + */ +function vote_delete_user_votes( $p_user_id ) +{ + if ( $p_user_id < 1 ) + { + error_parameters( $p_user_id ); + trigger_error( ERROR_USER_NOT_FOUND , ERROR ); + } + + $votes = vote_get_user_votes( null, true, $p_user_id ); + foreach($votes as $vote) + { + vote_delete($vote['issue_id'], $p_user_id); + } +} + +/** + * vote_get_user_votes + * + * @param integer $p_project_id + * @param boolean $p_include_resolved + * @param integer $p_user_id + * @return array issues and thier weight array('issue_id'=>$p_issue_id, 'weight'=>$p_weight); + */ +function vote_get_user_votes ($p_project_id = null, $p_include_resolved = true, $p_user_id = null) +{ + if ($p_user_id === null) + { + $p_user_id = auth_get_current_user_id(); + } + + $t_mantis_bug_votes_table = db_get_table( 'mantis_bug_votes_table' ); + + $query = "SELECT issue_id, weight FROM $t_mantis_bug_votes_table WHERE user_id = " . db_param(0); + $result = db_query_bound($query, Array($p_user_id)); + + $users = array(); + while ( $row = db_fetch_array( $result ) ) { + + $t_resolved = bug_is_resolved($row['issue_id']); + + if ($p_include_resolved || !$t_resolved ) + { + + if ( $p_project_id === null || $p_project_id == ALL_PROJECTS ) + { + $users[] = $row; + } + else + { + $t_issue_project = bug_get_field($row['issue_id'], 'project_id'); + if ( $t_issue_project == $p_project_id ) + { + $users[] = $row; + } + } + } + } + return $users; +} + +/** + * vote_get_issue_votes + * returns an array of user ids, weight + * + * @param integer $p_issue_id issue primary key + * @return array users and thier vote weight array('user_id'=>$p_user_id, 'weight'=>$p_weight); + */ +function vote_get_issue_votes( $p_issue_id ) +{ + if ( $p_issue_id < 1 ) + { + error_parameters( $p_issue_id ); + trigger_error( ERROR_BUG_NOT_FOUND , ERROR ); + } + + $t_mantis_bug_votes_table = db_get_table( 'mantis_bug_votes_table' ); + $query = "SELECT user_id, weight FROM $t_mantis_bug_votes_table WHERE issue_id = " . db_param(0); + $result = db_query_bound($query, Array($p_issue_id)); + $t_issue_votes = array(); + while ( $row = db_fetch_array( $result ) ) { + $t_issue_votes[] = $row; + } + return $t_issue_votes; +} + +/** + * vote_is_enabled + * check whether voting is enabled on the given project + * + * @param integer $p_project_id + * @return boolean + */ +function vote_is_enabled( $p_project_id = null ) +{ + if ($p_project_id === null) + { + $p_project_id = helper_get_current_project(); + } + $t_enabled = ( config_get( 'voting_enabled', null, null, $p_project_id ) == ON ); + return $t_enabled; +} + +/** + * vote_can_vote + * whether or not the user is allowed to vote on an issue + * + * @param integer $p_issue_id + * @param integer $p_user_id + * @return boolean + */ +function vote_can_vote( $p_issue_id, $p_user_id = null ) +{ + $t_can_vote = access_has_bug_level( config_get( 'voting_place_vote_threshold' ),$p_issue_id , $p_user_id ); + return $t_can_vote; +} + +/** + * vote_can_view_vote_details + * whether or not the user is allowed to view vote details + * + * @param integer $p_issue_id + * @param integer $p_user_id + * @return boolean + */ +function vote_can_view_vote_details( $p_issue_id, $p_user_id = null ) +{ + $t_has_level = ( access_has_bug_level( config_get( 'voting_view_user_votes_threshold' ), $p_issue_id , $p_user_id ) ); + return $t_has_level; +} + +/** + * vote_exists + * whether the user has placed a vote on a given issue or not + * @param integer $p_issue_id + * @param integer $p_user_id + * @return boolean + */ +function vote_exists ( $p_issue_id, $p_user_id ) +{ + $t_mantis_bug_votes_table = db_get_table( 'mantis_bug_votes_table' ); + $query = "SELECT COUNT(*) + FROM $t_mantis_bug_votes_table + WHERE issue_id=" . db_param(0) . " AND user_id = " . db_param(1); + $result = db_query_bound( $query, Array( $p_issue_id, $p_user_id ) ); + + if ( 0 == db_result( $result ) ) { + return false; + } else { + return true; + } +} + +/** + * vote_max_votes + * the maximum number of votes the given user can cast across all issues + * + * @param integer $p_user_id + * @return integer + */ +function vote_max_votes( $p_user_id ) +{ + $t_default_num_votes = config_get('voting_default_num_votes'); + + if (is_array($t_default_num_votes)) + { + ksort($t_default_num_votes); # relies on user levels being numeric + $t_user_level = user_get_access_level( $p_user_id ); + foreach($t_default_num_votes as $t_vote_level => $t_votes) + { + if ($t_user_level >= $t_vote_level) + { + $t_num_votes = $t_votes; + break; + } + } + } + else + { + $t_num_votes = intval($t_default_num_votes); + } + + return $t_num_votes; +} + +/** + * vote_available_votes + * the number of available votes a user can still cast on a given project + * note this may also return VOTES_UNLIMITED_VOTES so you should always test for this return value + * + * @param integer $p_project_id + * @param integer $p_user_id + * @return integer number of available votes, also VOTES_UNLIMITED_VOTES + */ +function vote_available_votes( $p_project_id = null, $p_user_id = null ) +{ + if ($p_user_id === null) + { + $p_user_id = auth_get_current_user_id(); + } + + $t_max_votes = vote_max_votes( $p_user_id ); + + + if ($t_max_votes == 0) + { + return VOTES_UNLIMITED_VOTES; + } + else + { + $t_used_votes = vote_used_votes( $p_project_id, $p_user_id ); + + $t_available_votes = $t_max_votes - $t_used_votes; + return $t_available_votes; + } +} + +/** + * vote_used_votes + * the number of votes already cast on a given project that are not resolved + * + * @param integer $p_project_id + * @param integer $p_user_id + * @return integer + */ +function vote_used_votes( $p_project_id = null, $p_user_id = null ) +{ + + if ($p_user_id === null) + { + $p_user_id = auth_get_current_user_id(); + } + + if ($p_project_id === null) + { + $p_project_id = helper_get_current_project(); + } + + $t_per_project = config_get('voting_per_project'); + + if ($t_per_project == ON) + { + $t_votes = vote_get_user_votes( $p_project_id, false, $p_user_id ); + } + else + { + $t_votes = vote_get_user_votes( ALL_PROJECTS , false, $p_user_id ); + } + + $t_weight_used = 0; + foreach($t_votes as $t_vote) + { + if ($t_vote['weight']>0) + { + $t_weight_used += $t_vote['weight']; + } + else + { + $t_weight_used -= $t_vote['weight']; + } + } + return $t_weight_used; +} + +/** + * vote_max_weight + * the maximum weight a user can cast on a single vote right now - note this is different from vote_available_votes + * takes in to consideration how many votes a user has remaining + * returning whichever is the lesser, your available votes or you max vote weight + * + * @param integer $p_project_id + * @param integer $p_user_id + * @return integer + */ +function vote_max_weight( $p_project_id = null, $p_user_id = null ) +{ + if ($p_project_id === null) + { + $p_project_id = helper_get_current_project(); + } + + if ($p_user_id === null) + { + $p_user_id = auth_get_current_user_id(); + } + + $t_available_votes = vote_available_votes( $p_project_id, $p_user_id ); + $t_voting_max_vote_weight = config_get('voting_max_vote_weight'); + if (is_array($t_voting_max_vote_weight)) + { + ksort($t_voting_max_vote_weight); # relies on user levels being numeric + $t_user_level = user_get_access_level( $p_user_id ); + + #find your maximum applicable voting weight + foreach($t_voting_max_vote_weight as $t_level => $t_max) + { + if ($t_user_level >= $t_level) + { + $t_voting_max_vote_weight = $t_max; + } + } + } + + # return whichever is the lesser, your available votes or you max vote weight + $t_voting_max_vote_weight = ($t_available_votes > $t_voting_max_vote_weight)?$t_voting_max_vote_weight:$t_available_votes; + + return $t_voting_max_vote_weight; +} + +/** + * vote_updates_counters_for_issue + * updates a given issue/bug vote weight counters + * should be called post any changes to votes on an issue + * + * @param integer $p_issue_id + */ +function vote_updates_counters_for_issue( $p_issue_id ) +{ + $c_issue_id = db_prepare_int( $p_issue_id ); + $t_issue_table = db_get_table( 'mantis_bug_votes_table' ); + + $t_bug = bug_get( $p_issue_id ); + + $query = "SELECT COUNT(*) as voteCount + FROM $t_issue_table + WHERE issue_id=" . db_param(); + $t_count_result = db_query_bound( $query, Array( $c_issue_id ) ); + $t_bug->votes_num_voters = (int)$t_count_result->fields['voteCount']; + + $query = "SELECT SUM(weight) as voteWeight + FROM $t_issue_table + WHERE weight > 0 AND issue_id=" . db_param(); + $t_positive_result = db_query_bound( $query, Array( $c_issue_id ) ); + $t_bug->votes_positive = (int)$t_positive_result->fields['voteWeight']; + + $query = "SELECT SUM(weight) as voteWeight + FROM $t_issue_table + WHERE weight < 0 AND issue_id=" . db_param(); + $t_negative_result = db_query_bound( $query, Array( $c_issue_id ) ); + $t_bug->votes_negative = (int)$t_negative_result->fields['voteWeight']; + + bug_update( $p_issue_id, $t_bug, false, true ); +} Index: css/default.css =================================================================== --- css/default.css (revision 5500) +++ css/default.css (working copy) @@ -165,3 +165,28 @@ .progress400 { position: relative; width: 400px; border: 1px solid #d7d7d7; margin-top: 1em; margin-bottom: 1em; padding: 1px; } .progress400 .bar { display: block; position: relative; background: #6bba70; text-align: center; font-weight: normal; color: #333; height: 2em; line-height: 2em; } + +table.bugList +{ + width: 100%; border: solid 1px #000; + margin:0; + margin-bottom: 16px; + caption-side: top; + +} +table.bugList caption +{ + font-weight:bold; + font-style:italic; + text-align:left; + border: 1px solid #000; + border-bottom:0; + padding: 4px; + margin-top:16px; +} +table.bugList tfoot tr td +{ + background-color: #ccc; + text-align: right; +} + Index: lang/strings_english.txt =================================================================== --- lang/strings_english.txt (revision 5500) +++ lang/strings_english.txt (working copy) @@ -318,6 +318,8 @@ $MANTIS_ERROR[ERROR_SESSION_VAR_NOT_FOUND] = 'Session variable \'%s\' not found.'; $MANTIS_ERROR[ERROR_FORM_TOKEN_INVALID] = 'Invalid form security token. Did you submit the form twice by accident?'; $MANTIS_ERROR[ERROR_INVALID_REQUEST_METHOD] = 'This page cannot be accessed using this method.'; +$MANTIS_ERROR[ERROR_VOTING_OVER_LIMIT] = 'not allowed to vote more than your limit, or have a vote with weight 0'; +$MANTIS_ERROR[ERROR_VOTING_NOT_ENABLED] = 'voting is not enabled'; $s_login_error = 'Your account may be disabled or blocked or the username/password you entered is incorrect.'; $s_login_cookies_disabled = 'Your browser either doesn\'t know how to handle cookies, or refuses to handle them.'; @@ -641,6 +643,7 @@ # bug_vote_add.php $s_vote_added_msg = 'Vote has been added...'; +$s_vote_removed_msg = 'Vote has been removed...'; # bugnote_add.php $s_bugnote_added_msg = 'Note added...'; @@ -1519,6 +1522,30 @@ $s_copy_columns_from = 'Copy Columns From'; $s_copy_columns_to = 'Copy Columns To'; +# Voting +$s_vote_cast_button = 'Cast Vote:'; +$s_vote_delete_button = 'Delete My Vote'; +$s_bugvote_added = 'Vote Added'; +$s_bugvote_deleted = 'Vote Deleted'; +$s_votes_positive = 'Votes Positive'; +$s_votes_negative = 'Votes Negative'; +$s_voted_by = 'Voted By'; +$s_vote_balance = 'Vote Balance'; +$s_vote_num_voters = '# Voters'; +$s_votes_remain = 'votes remaining'; +$s_votes_used = 'votes used'; +$s_voting_this_issue = 'Users voting for this issue'; +$s_votes_num_voters = 'Number of Voters'; +$s_my_votes = 'My Votes'; +$s_own_voted = 'Issues you have voted for:'; +$s_voting_hide = 'Hide Resolved'; +$s_voting_show = 'Show All'; +$s_vote_weight = 'Vote Weight'; +$s_no_votes = 'No votes available'; +$s_voted_and_assigned = 'You voted for this issue and it is now being worked on. You will be able to reuse the voting credits you spent on this issue once it is resolved.'; +$s_voted_and_resolved = 'You voted for this issue, and it is now resolved. Your voting credits have been returned to you.'; +$s_vote_unlimited = 'unlimited'; + # due date $s_due_date = "Due Date"; $s_overdue = "Overdue"; Index: view_all_set.php =================================================================== --- view_all_set.php (revision 5500) +++ view_all_set.php (working copy) @@ -190,6 +190,14 @@ $f_user_monitor = gpc_get_string( FILTER_PROPERTY_MONITOR_USER_ID, META_FILTER_ANY ); $f_user_monitor = array( $f_user_monitor ); } + + $f_user_votes = array(); + if ( is_array( gpc_get( 'user_votes', null ) ) ) { + $f_user_votes = gpc_get_string_array( 'user_votes', META_FILTER_ANY ); + } else { + $f_user_votes = gpc_get_string( 'user_votes', META_FILTER_ANY ); + $f_user_votes = array( $f_user_votes ); + } $f_note_user_id = array(); if ( is_array( gpc_get( FILTER_PROPERTY_NOTE_USER_ID, null ) ) ) { @@ -428,6 +436,7 @@ $t_setting_arr[ FILTER_PROPERTY_TARGET_VERSION ] = $f_target_version; $t_setting_arr[ FILTER_PROPERTY_PRIORITY_ID ] = $f_show_priority; $t_setting_arr[ FILTER_PROPERTY_MONITOR_USER_ID ] = $f_user_monitor; + $t_setting_arr['user_votes'] = $f_user_votes; $t_setting_arr[ FILTER_PROPERTY_VIEW_STATE_ID ] = $f_view_state; $t_setting_arr['custom_fields'] = $f_custom_fields_data; $t_setting_arr[ FILTER_PROPERTY_SHOW_STICKY_ISSUES ] = $f_sticky_issues; @@ -482,6 +491,7 @@ $t_setting_arr[ FILTER_PROPERTY_TARGET_VERSION ] = array( META_FILTER_ANY ); $t_setting_arr[ FILTER_PROPERTY_MONITOR_USER_ID ] = array( META_FILTER_ANY ); $t_setting_arr[ FILTER_PROPERTY_NOTE_USER_ID ] = array( META_FILTER_ANY ); + $t_setting_arr['user_votes'] = array( META_FILTER_ANY ); $t_setting_arr[ FILTER_PROPERTY_RELATIONSHIP_TYPE ] = -1; $t_setting_arr[ FILTER_PROPERTY_RELATIONSHIP_BUG ] = 0;