diff -urN --exclude=CVS --exclude=.svn --exclude=.swp --exclude='.git*' --exclude=config_inc.php mantis-cvs/admin/schema.php mantis-tagging/admin/schema.php --- mantis-cvs/admin/schema.php 2007-08-07 10:54:56.000000000 -0400 +++ mantis-tagging/admin/schema.php 2007-08-17 15:19:22.000000000 -0400 @@ -330,6 +330,21 @@ $upgrade[] = Array('CreateIndexSQL',Array('idx_diskfile',config_get('mantis_bug_file_table'),'diskfile')); $upgrade[] = Array('AlterColumnSQL', Array( config_get( 'mantis_user_print_pref_table' ), "print_pref C(64) NOTNULL" ) ); $upgrade[] = Array('AlterColumnSQL', Array( config_get( 'mantis_bug_history_table' ), "field_name C(64) NOTNULL" ) ); +$upgrade[] = Array('CreateTableSQL', Array( config_get( 'mantis_tag_table' ), " + id I UNSIGNED NOTNULL PRIMARY AUTOINCREMENT, + user_id I UNSIGNED NOTNULL DEFAULT '0', + name C(100) NOTNULL DEFAULT \" '' \", + description XL NOTNULL, + date_created T NOTNULL DEFAULT '1970-01-01 00:00:01', + date_updated T NOTNULL DEFAULT '1970-01-01 00:00:01' + ", Array( 'mysql' => 'TYPE=MyISAM', 'pgsql' => 'WITHOUT OIDS' ) ) ); +$upgrade[] = Array('CreateIndexSQL', Array( 'idx_tag_name', config_get( 'mantis_tag_table' ), 'name', Array( 'UNIQUE' ) ) ); +$upgrade[] = Array('CreateTableSQL', Array( config_get( 'mantis_bug_tag_table' ), " + bug_id I UNSIGNED NOTNULL PRIMARY DEFAULT '0', + tag_id I UNSIGNED NOTNULL PRIMARY DEFAULT '0', + user_id I UNSIGNED NOTNULL DEFAULT '0', + date_attached T NOTNULL DEFAULT '1970-01-01 00:00:01' + ", Array( 'mysql' => 'TYPE=MyISAM', 'pgsql' => 'WITHOUT OIDS' ) ) ); # Release marker: 1.1.0a4 diff -urN --exclude=CVS --exclude=.svn --exclude=.swp --exclude='.git*' --exclude=config_inc.php mantis-cvs/bug_view_advanced_page.php mantis-tagging/bug_view_advanced_page.php --- mantis-cvs/bug_view_advanced_page.php 2007-08-07 10:47:14.000000000 -0400 +++ mantis-tagging/bug_view_advanced_page.php 2007-08-17 11:29:28.000000000 -0400 @@ -20,6 +20,7 @@ require_once( $t_core_path.'date_api.php' ); require_once( $t_core_path.'relationship_api.php' ); require_once( $t_core_path.'last_visited_api.php' ); + require_once( $t_core_path.'tag_api.php' ); $f_bug_id = gpc_get_int( 'bug_id' ); $f_history = gpc_get_bool( 'history', config_get( 'history_default_visible' ) ); @@ -464,13 +465,35 @@ + + +> + + + + + + + + +> + + + + + + + - + + +> + + + + + + + + +> + + + + + + + diff -urN --exclude=CVS --exclude=.svn --exclude=.swp --exclude='.git*' --exclude=config_inc.php mantis-cvs/config_defaults_inc.php mantis-tagging/config_defaults_inc.php --- mantis-cvs/config_defaults_inc.php 2007-08-16 13:23:56.000000000 -0400 +++ mantis-tagging/config_defaults_inc.php 2007-08-16 13:48:44.000000000 -0400 @@ -1331,6 +1331,7 @@ $g_mantis_bug_monitor_table = '%db_table_prefix%_bug_monitor%db_table_suffix%'; $g_mantis_bug_relationship_table = '%db_table_prefix%_bug_relationship%db_table_suffix%'; $g_mantis_bug_table = '%db_table_prefix%_bug%db_table_suffix%'; + $g_mantis_bug_tag_table = '%db_table_prefix%_bug_tag%db_table_suffix%'; $g_mantis_bug_text_table = '%db_table_prefix%_bug_text%db_table_suffix%'; $g_mantis_bugnote_table = '%db_table_prefix%_bugnote%db_table_suffix%'; $g_mantis_bugnote_text_table = '%db_table_prefix%_bugnote_text%db_table_suffix%'; @@ -1340,6 +1341,7 @@ $g_mantis_project_table = '%db_table_prefix%_project%db_table_suffix%'; $g_mantis_project_user_list_table = '%db_table_prefix%_project_user_list%db_table_suffix%'; $g_mantis_project_version_table = '%db_table_prefix%_project_version%db_table_suffix%'; + $g_mantis_tag_table = '%db_table_prefix%_tag%db_table_suffix%'; $g_mantis_user_table = '%db_table_prefix%_user%db_table_suffix%'; $g_mantis_user_profile_table = '%db_table_prefix%_user_profile%db_table_suffix%'; $g_mantis_user_pref_table = '%db_table_prefix%_user_pref%db_table_suffix%'; @@ -1835,6 +1837,34 @@ $g_recently_visited_count = 5; ##################### + # Bug Tagging + ##################### + + # String that will separate tags as entered for input + $g_tag_separator = ','; + + # Access level required to view tags attached to a bug + $g_tag_view_threshold = VIEWER; + + # Access level required to attach tags to a bug + $g_tag_attach_threshold = REPORTER; + + # Access level required to detach tags from a bug + $g_tag_detach_threshold = DEVELOPER; + + # Access level required to detach tags attached by the same user + $g_tag_detach_own_threshold = REPORTER; + + # Access level required to create new tags + $g_tag_create_threshold = REPORTER; + + # Access level required to edit tag names and descriptions + $g_tag_edit_threshold = DEVELOPER; + + # Access level required to edit descriptions by the creating user + $g_tag_edit_own_threshold = REPORTER; + + ##################### # Time tracking ##################### diff -urN --exclude=CVS --exclude=.svn --exclude=.swp --exclude='.git*' --exclude=config_inc.php mantis-cvs/core/constant_inc.php mantis-tagging/core/constant_inc.php --- mantis-cvs/core/constant_inc.php 2007-08-07 10:47:13.000000000 -0400 +++ mantis-tagging/core/constant_inc.php 2007-08-16 13:48:43.000000000 -0400 @@ -153,6 +153,9 @@ define( 'CHECKIN', 22 ); define( 'BUG_REPLACE_RELATIONSHIP', 23 ); define( 'BUG_PAID_SPONSORSHIP', 24 ); + define( 'TAG_ATTACHED', 25 ); + define( 'TAG_DETACHED', 26 ); + define( 'TAG_RENAMED', 27 ); # bug relationship constants define( 'BUG_DUPLICATE', 0 ); diff -urN --exclude=CVS --exclude=.svn --exclude=.swp --exclude='.git*' --exclude=config_inc.php mantis-cvs/core/filter_api.php mantis-tagging/core/filter_api.php --- mantis-cvs/core/filter_api.php 2007-08-07 10:47:13.000000000 -0400 +++ mantis-tagging/core/filter_api.php 2007-08-17 23:52:18.000000000 -0400 @@ -16,6 +16,7 @@ require_once( $t_core_dir . 'bug_api.php' ); require_once( $t_core_dir . 'collapse_api.php' ); require_once( $t_core_dir . 'relationship_api.php' ); + require_once( $t_core_dir . 'tag_api.php' ); ########################################################################### # Filter Property Names @@ -56,6 +57,8 @@ define( 'FILTER_PROPERTY_FILTER_BY_DATE', 'do_filter_by_date' ); define( 'FILTER_PROPERTY_RELATIONSHIP_TYPE', 'relationship_type' ); define( 'FILTER_PROPERTY_RELATIONSHIP_BUG', 'relationship_bug' ); + define( 'FILTER_PROPERTY_TAG_STRING', 'tag_string' ); + define( 'FILTER_PROPERTY_TAG_SELECT', 'tag_select' ); ########################################################################### # Filter Query Parameter Names @@ -96,6 +99,8 @@ define( 'FILTER_SEARCH_FILTER_BY_DATE', 'filter_by_date' ); define( 'FILTER_SEARCH_RELATIONSHIP_TYPE', 'relationship_type' ); define( 'FILTER_SEARCH_RELATIONSHIP_BUG', 'relationship_bug' ); + define( 'FILTER_SEARCH_TAG_STRING', 'tag_string' ); + define( 'FILTER_SEARCH_TAG_SELECT', 'tag_select' ); # Checks the supplied value to see if it is an ANY value. # $p_field_value - The value to check. @@ -306,6 +311,14 @@ $t_query[] = filter_encode_field_and_value( FILTER_SEARCH_OS_BUILD, $p_custom_filter[FILTER_PROPERTY_OS_BUILD] ); } + if ( !filter_str_field_is_any( $p_custom_filter[FILTER_PROPERTY_TAG_STRING] ) ) { + $t_query[] = filter_encode_field_and_value( FILTER_SEARCH_TAG_STRING, $p_custom_filter[FILTER_PROPERTY_TAG_STRING] ); + } + + if ( !filter_str_field_is_any( $p_custom_filter[FILTER_PROPERTY_TAG_SELECT] ) ) { + $t_query[] = filter_encode_field_and_value( FILTER_SEARCH_TAG_SELECT, $p_custom_filter[FILTER_PROPERTY_TAG_SELECT] ); + } + if ( isset( $p_custom_filter['custom_fields'] ) ) { foreach( $p_custom_filter['custom_fields'] as $t_custom_field_id => $t_custom_field_values ) { if ( !filter_str_field_is_any( $t_custom_field_values ) ) { @@ -1057,6 +1070,63 @@ array_push( $t_where_clauses, '('. implode( ' OR ', $t_clauses ) .')' ); } + # tags + $c_tag_string = trim( $t_filter['tag_string'] ); + if ( !is_blank( $c_tag_string ) ) { + require_once( $t_core_path . 'tag_api.php' ); + $t_tags = tag_parse_filters( $c_tag_string ); + + if ( !count( $t_tags ) ) { break; } + + $t_tags_all = array(); + $t_tags_any = array(); + $t_tags_none = array(); + + foreach( $t_tags as $t_tag_row ) { + switch ( $t_tag_row['filter'] ) { + case 1: + $t_tags_all[] = $t_tag_row; + break; + case 0: + $t_tags_any[] = $t_tag_row; + break; + case -1: + $t_tags_none[] = $t_tag_row; + break; + } + } + + if ( 0 < $t_filter['tag_select'] && tag_exists( $t_filter['tag_select'] ) ) { + $t_tags_any[] = tag_get( $t_filter['tag_select'] ); + } + + $t_bug_tag_table = config_get( 'mantis_bug_tag_table' ); + + if ( count( $t_tags_all ) ) { + $t_clauses = array(); + foreach ( $t_tags_all as $t_tag_row ) { + array_push( $t_clauses, "$t_bug_table.id IN ( SELECT bug_id FROM $t_bug_tag_table WHERE $t_bug_tag_table.tag_id = $t_tag_row[id] )" ); + } + array_push( $t_where_clauses, '('. implode( ' AND ', $t_clauses ) .')' ); + } + + if ( count( $t_tags_any ) ) { + $t_clauses = array(); + foreach ( $t_tags_any as $t_tag_row ) { + array_push( $t_clauses, "$t_bug_tag_table.tag_id = $t_tag_row[id]" ); + } + array_push( $t_where_clauses, "$t_bug_table.id IN ( SELECT bug_id FROM $t_bug_tag_table WHERE ( ". implode( ' OR ', $t_clauses ) .') )' ); + } + + if ( count( $t_tags_none ) ) { + $t_clauses = array(); + foreach ( $t_tags_none as $t_tag_row ) { + array_push( $t_clauses, "$t_bug_tag_table.tag_id = $t_tag_row[id]" ); + } + array_push( $t_where_clauses, "$t_bug_table.id NOT IN ( SELECT bug_id FROM $t_bug_tag_table WHERE ( ". implode( ' OR ', $t_clauses ) .') )' ); + } + } + # custom field filters if( ON == config_get( 'filter_by_custom_fields' ) ) { # custom field filtering @@ -2291,6 +2361,7 @@ : + : 8 ) { echo ' '; @@ -2312,7 +2383,16 @@ print_multivalue_field( FILTER_PROPERTY_OS_BUILD, $t_filter[FILTER_PROPERTY_OS_BUILD] ); ?> - + + + + + + + ' . $p_new_value; + break; } } diff -urN --exclude=CVS --exclude=.svn --exclude=.swp --exclude='.git*' --exclude=config_inc.php mantis-cvs/core/html_api.php mantis-tagging/core/html_api.php --- mantis-cvs/core/html_api.php 2007-08-07 10:47:13.000000000 -0400 +++ mantis-tagging/core/html_api.php 2007-08-17 11:56:56.000000000 -0400 @@ -1218,4 +1218,24 @@ echo ''; } + + function html_button_tag_update( $p_tag_id ) { + if ( access_has_global_level( config_get( 'tag_edit_threshold' ) ) + || ( auth_get_current_user_id() == tag_get_field( $p_tag_id, 'user_id' ) + && access_has_global_level( config_get( 'tag_edit_own_threshold' ) ) ) ) + { + html_button( 'tag_update_page.php', lang_get( 'tag_update_button' ), array( 'tag_id' => $p_tag_id ) ); + } + } + + function html_button_tag_delete( $p_tag_id ) { + if ( access_has_global_level( config_get( 'tag_edit_threshold' ) ) ) { + html_button( 'tag_delete.php', lang_get( 'tag_delete_button' ), array( 'tag_id' => $p_tag_id ) ); + } + } + + function html_buttons_tag_view_page( $p_tag_id ) { + html_button_tag_update( $p_tag_id ); + html_button_tag_delete( $p_tag_id ); + } ?> diff -urN --exclude=CVS --exclude=.svn --exclude=.swp --exclude='.git*' --exclude=config_inc.php mantis-cvs/core/print_api.php mantis-tagging/core/print_api.php --- mantis-cvs/core/print_api.php 2007-08-14 10:12:41.000000000 -0400 +++ mantis-tagging/core/print_api.php 2007-08-17 11:02:32.000000000 -0400 @@ -11,6 +11,7 @@ $t_core_dir = dirname( __FILE__ ).DIRECTORY_SEPARATOR; + require_once( $t_core_dir . 'ajax_api.php' ); require_once( $t_core_dir . 'current_user_api.php' ); require_once( $t_core_dir . 'string_api.php' ); require_once( $t_core_dir . 'prepare_api.php' ); @@ -255,6 +256,54 @@ PRINT ""; } } + + function print_tag_attach_form( $p_bug_id, $p_string="" ) { + ?> + +
+ + + +
+ + + + + ',lang_get( 'tag_existing' ),''; + while ( $row = db_fetch_array( $result ) ) { + echo ''; + } + } + # -------------------- # Get current headlines and id prefix with v_ function print_news_item_option_list() { diff -urN --exclude=CVS --exclude=.svn --exclude=.swp --exclude='.git*' --exclude=config_inc.php mantis-cvs/core/tag_api.php mantis-tagging/core/tag_api.php --- mantis-cvs/core/tag_api.php 1969-12-31 19:00:00.000000000 -0500 +++ mantis-tagging/core/tag_api.php 2007-08-20 09:34:48.000000000 -0400 @@ -0,0 +1,474 @@ + 0; + } + + function tag_ensure_exists( $p_tag_id ) { + if ( !tag_exists( $p_tag_id ) ) { + error_parameters( $p_tag_id ); + trigger_error( ERROR_TAG_NOT_FOUND, ERROR ); + } + } + + function tag_is_unique( $p_name ) { + $c_name = trim( db_prepare_string( $p_name ) ); + $t_tag_table = config_get( 'mantis_tag_table' ); + + $query = "SELECT id FROM $t_tag_table WHERE name like '$c_name'"; + $result = db_query( $query ) ; + + return db_num_rows( $result ) == 0; + } + + function tag_ensure_unique( $p_name ) { + if ( !tag_is_unique( $p_name ) ) { + trigger_error( ERROR_TAG_DUPLICATE, ERROR ); + } + } + + # Name must start with letter/number and consist of letters, numbers, hyphen, underscore, period, or spaces + function tag_name_is_valid( $p_name, $p_prefix="", &$p_matches=null ) { + $t_pattern = "/^$p_prefix([a-zA-Z0-9][a-zA-Z0-9-_. ]*)$/"; + return preg_match( $t_pattern, $p_name, $p_matches ); + } + + function tag_ensure_name_is_valid( $p_name ) { + if ( !tag_name_is_valid( $p_name ) ) { + trigger_error( ERROR_TAG_NAME_INVALID, ERROR ); + } + } + + function tag_cmp_name( $p_tag1, $p_tag2 ) { + return strcasecmp( $p_tag1['name'], $p_tag2['name'] ); + } + + function tag_parse_string( $p_string ) { + $t_tags = array(); + + $t_strings = explode( config_get( 'tag_separator' ), $p_string ); + foreach( $t_strings as $t_name ) { + $t_name = trim( $t_name ); + if ( "" == trim( $t_name ) ) { continue; } + + $t_tag_row = tag_get_by_name( $t_name ); + if ( $t_tag_row !== false ) { + $t_tags[] = $t_tag_row; + } else { + if ( tag_name_is_valid( $t_name ) ) { + $t_id = -1; + } else { + $t_id = -2; + } + $t_tags[] = array( 'id' => $t_id, 'name' => $t_name ); + } + } + usort( $t_tags, "tag_cmp_name" ); + return $t_tags; + } + + function tag_parse_filters( $p_string ) { + $t_tags = array(); + $t_prefix = "[+-]{0,1}"; + + $t_strings = explode( config_get( 'tag_separator' ), $p_string ); + foreach( $t_strings as $t_name ) { + $t_name = trim( $t_name ); + if ( "" == trim( $t_name ) || !tag_name_is_valid( $t_name, $t_prefix ) ) { continue; } + + $t_matches = array(); + if ( tag_name_is_valid( $t_name, $t_prefix, $t_matches ) ) { + $t_tag_row = tag_get_by_name( $t_matches[1] ); + if ( $t_tag_row !== false ) { + $t_filter = substr( $t_name, 0, 1 ); + + if ( "+" == $t_filter ) { + $t_tag_row['filter'] = 1; + } elseif ( "-" == $t_filter ) { + $t_tag_row['filter'] = -1; + } else { + $t_tag_row['filter'] = 0; + } + + $t_tags[] = $t_tag_row; + } + } + } + usort( $t_tags, "tag_cmp_name" ); + return $t_tags; + } + + # CRUD + + function tag_get( $p_tag_id ) { + tag_ensure_exists( $p_tag_id ); + + $c_tag_id = db_prepare_int( $p_tag_id ); + + $t_tag_table = config_get( 'mantis_tag_table' ); + + $query = "SELECT * FROM $t_tag_table + WHERE id='$c_tag_id'"; + $result = db_query( $query ); + + if ( 0 == db_num_rows( $result ) ) { + return false; + } + $row = db_fetch_array( $result ); + + return $row; + } + + function tag_get_by_name( $p_name ) { + $c_name = db_prepare_string( $p_name ); + + $t_tag_table = config_get( 'mantis_tag_table' ); + + $query = "SELECT * FROM $t_tag_table + WHERE name LIKE '$c_name'"; + $result = db_query( $query ); + + if ( 0 == db_num_rows( $result ) ) { + return false; + } + $row = db_fetch_array( $result ); + + return $row; + } + + function tag_get_field( $p_tag_id, $p_field_name ) { + $row = tag_get( $p_tag_id ); + + if ( isset( $row[$p_field_name] ) ) { + return $row[$p_field_name]; + } else { + error_parameters( $p_field_name ); + trigger_error( ERROR_DB_FIELD_NOT_FOUND, WARNING ); + return ''; + } + } + + function tag_create( $p_name, $p_user_id, $p_description='' ) { + + tag_ensure_name_is_valid( $p_name ); + tag_ensure_unique( $p_name ); + + $c_name = trim( db_prepare_string( $p_name ) ); + $c_description = db_prepare_string( $p_description ); + $c_user_id = db_prepare_int( $p_user_id ); + $c_date_created = db_now(); + + $t_tag_table = config_get( 'mantis_tag_table' ); + + $query = "INSERT INTO $t_tag_table + ( user_id, + name, + description, + date_created, + date_updated + ) + VALUES + ( '$c_user_id', + '$c_name', + '$c_description', + ".$c_date_created.", + ".$c_date_created." + )"; + + db_query( $query ); + return db_insert_id( $t_tag_table ); + } + + function tag_update( $p_tag_id, $p_name, $p_user_id, $p_description ) { + tag_ensure_exists( $p_tag_id ); + user_ensure_exists( $p_user_id ); + + tag_ensure_name_is_valid( $p_name ); + + $t_tag_name = tag_get_field( $p_tag_id, 'name' ); + + $t_rename = false; + if ( strtolower($p_name) != strtolower($t_tag_name) ) { + tag_ensure_unique( $p_name ); + $t_rename = true; + } + + $c_tag_id = trim( db_prepare_int( $p_tag_id ) ); + $c_user_id = db_prepare_string( $p_user_id ); + $c_name = db_prepare_string( $p_name ); + $c_description = db_prepare_string( $p_description ); + $c_date_updated = db_now(); + + $t_tag_table = config_get( 'mantis_tag_table' ); + + $query = "UPDATE $t_tag_table + SET user_id='$c_user_id', + name='$c_name', + description='$c_description', + date_updated=".$c_date_updated." + WHERE id='$c_tag_id'"; + db_query( $query ); + + if ( $t_rename ) { + $t_bugs = tag_get_bugs_attached( $p_tag_id ); + + foreach ( $t_bugs as $t_bug_id ) { + history_log_event_special( $t_bug_id, TAG_RENAMED, $t_tag_name, $c_name ); + } + } + + return true; + } + + function tag_delete( $p_tag_id ) { + tag_ensure_exists( $p_tag_id ); + + $t_bugs = tag_get_bugs_attached( $p_tag_id ); + foreach ( $t_bugs as $t_bug_id ) { + tag_bug_detach( $p_tag_id, $t_bug_id ); + } + + $c_tag_id = db_prepare_int( $p_tag_id ); + + $t_tag_table = config_get( 'mantis_tag_table' ); + $t_bug_tag_table = config_get( 'mantis_bug_tag_table' ); + + $query = "DELETE FROM $t_tag_table + WHERE id='$c_tag_id'"; + db_query( $query ); + + return true; + } + + # Associative + + function tag_bug_is_attached( $p_tag_id, $p_bug_id ) { + $c_tag_id = db_prepare_int( $p_tag_id ); + $c_bug_id = db_prepare_int( $p_bug_id ); + + $t_bug_tag_table= config_get( 'mantis_bug_tag_table' ); + + $query = "SELECT * FROM $t_bug_tag_table + WHERE tag_id='$c_tag_id' AND bug_id='$c_bug_id'"; + $result = db_query( $query ); + return ( db_num_rows( $result ) > 0 ); + } + + function tag_bug_get_row( $p_tag_id, $p_bug_id ) { + $c_tag_id = db_prepare_int( $p_tag_id ); + $c_bug_id = db_prepare_int( $p_bug_id ); + + $t_bug_tag_table= config_get( 'mantis_bug_tag_table' ); + + $query = "SELECT * FROM $t_bug_tag_table + WHERE tag_id='$c_tag_id' AND bug_id='$c_bug_id'"; + $result = db_query( $query ); + + if ( db_num_rows( $result ) == 0 ) { + trigger_error( TAG_NOT_ATTACHED, ERROR ); + } + return db_fetch_array( $result ); + } + + function tag_bug_get_attached( $p_bug_id ) { + $c_bug_id = db_prepare_int( $p_bug_id ); + + $t_tag_table = config_get( 'mantis_tag_table' ); + $t_bug_tag_table= config_get( 'mantis_bug_tag_table' ); + + $query = "SELECT t.*, b.user_id as user_attached, b.date_attached + FROM $t_tag_table as t + LEFT JOIN $t_bug_tag_table as b + on t.id=b.tag_id + WHERE b.bug_id='$c_bug_id'"; + $result = db_query( $query ); + + $rows = array(); + while ( $row = db_fetch_array( $result ) ) { + $rows[] = $row; + } + + usort( $rows, "tag_cmp_name" ); + return $rows; + } + + function tag_get_bugs_attached( $p_tag_id ) { + $c_tag_id = db_prepare_int( $p_tag_id ); + + $t_bug_tag_table= config_get( 'mantis_bug_tag_table' ); + + $query = "SELECT bug_id FROM $t_bug_tag_table + WHERE tag_id='$c_tag_id'"; + $result = db_query( $query ); + + $bugs = array(); + while ( $row = db_fetch_array( $result ) ) { + $bugs[] = $row['bug_id']; + } + + return $bugs; + } + + function tag_bug_attach( $p_tag_id, $p_bug_id, $p_user_id ) { + tag_ensure_exists( $p_tag_id ); + bug_ensure_exists( $p_bug_id ); + user_ensure_exists( $p_user_id ); + + if ( tag_bug_is_attached( $p_tag_id, $p_bug_id ) ) { + trigger_error( TAG_ALREADY_ATTACHED, ERROR ); + } + + $c_tag_id = db_prepare_int( $p_tag_id ); + $c_bug_id = db_prepare_int( $p_bug_id ); + $c_user_id = db_prepare_int( $p_user_id ); + $c_date_attached= db_now(); + + $t_bug_tag_table= config_get( 'mantis_bug_tag_table' ); + + $query = "INSERT INTO $t_bug_tag_table + ( tag_id, + bug_id, + user_id, + date_attached + ) + VALUES + ( '$c_tag_id', + '$c_bug_id', + '$c_user_id', + ".$c_date_attached." + )"; + db_query( $query ); + + $t_tag_name = tag_get_field( $p_tag_id, 'name' ); + history_log_event_special( $p_bug_id, TAG_ATTACHED, $t_tag_name ); + + return true; + } + + function tag_bug_detach( $p_tag_id, $p_bug_id ) { + tag_ensure_exists( $p_tag_id ); + bug_ensure_exists( $p_bug_id ); + + if ( !tag_bug_is_attached( $p_tag_id, $p_bug_id ) ) { + trigger_error( TAG_NOT_ATTACHED, ERROR ); + } + + $c_tag_id = db_prepare_int( $p_tag_id ); + $c_bug_id = db_prepare_int( $p_bug_id ); + + $t_bug_tag_table= config_get( 'mantis_bug_tag_table' ); + + $query = "DELETE FROM $t_bug_tag_table + WHERE tag_id='$c_tag_id' AND bug_id='$c_bug_id'"; + db_query( $query ); + + $t_tag_name = tag_get_field( $p_tag_id, 'name' ); + history_log_event_special( $p_bug_id, TAG_DETACHED, $t_tag_name ); + + return true; + } + + # Display + + function tag_display_link( $p_tag_row, $p_bug_id=0 ) { + if ( auth_get_current_user_id() == $p_tag_row[user_attached] ) { + $t_detach = config_get( 'tag_detach_own_threshold' ); + } else { + $t_detach = config_get( 'tag_detach_threshold' ); + } + + $t_name = string_display_line( $p_tag_row['name'] ); + $t_description = string_display_line( $p_tag_row['description'] ); + + echo "$t_name"; + + if ( access_has_global_level($t_detach) ) { + $t_tooltip = sprintf( lang_get( 'tag_detach' ), $t_name ); + echo " [x]"; + } + + return true; + } + + function tag_display_attached( $p_bug_id ) { + $t_tag_rows = tag_bug_get_attached( $p_bug_id ); + + if ( count( $t_tag_rows ) == 0 ) { + echo lang_get( 'tag_none_attached' ); + } else { + $i = 0; + foreach ( $t_tag_rows as $t_tag ) { + echo ( $i > 0 ? config_get('tag_separator')." " : "" ); + tag_display_link( $t_tag, $p_bug_id ); + $i++; + } + } + + return true; + } + + # Statistics + + function tag_stats_attached( $p_tag_id ) { + $c_tag_id = db_prepare_int( $p_tag_id ); + $t_bug_tag_table = config_get( 'mantis_bug_tag_table' ); + + $query = "SELECT COUNT(*) FROM $t_bug_tag_table + WHERE tag_id='$c_tag_id'"; + $result = db_query( $query ); + + return db_result( $result ); + } + + function tag_stats_related( $p_tag_id, $p_limit=5 ) { + $t_tag_table = config_get( 'mantis_tag_table' ); + $t_bug_tag_table = config_get( 'mantis_bug_tag_table' ); + $c_tag_id = db_prepare_int( $p_tag_id ); + + $query = "SELECT * FROM $t_bug_tag_table + WHERE tag_id != $c_tag_id + AND bug_id IN ( SELECT bug_id FROM $t_bug_tag_table + WHERE tag_id=$c_tag_id ) "; + $result = db_query( $query ); + + $t_tag_counts = array(); + while ( $row = db_fetch_array( $result ) ) { + $t_tag_counts[$row['tag_id']]++; + } + + arsort( $t_tag_counts ); + + $t_tags = array(); + $i = 1; + foreach ( $t_tag_counts as $t_tag_id => $t_count ) { + $t_tag_row = tag_get($t_tag_id); + $t_tag_row['count'] = $t_count; + $t_tags[] = $t_tag_row; + $i++; + if ( $i > $p_limit ) { break; } + } + + return $t_tags; + } diff -urN --exclude=CVS --exclude=.svn --exclude=.swp --exclude='.git*' --exclude=config_inc.php mantis-cvs/core/xmlhttprequest_api.php mantis-tagging/core/xmlhttprequest_api.php --- mantis-cvs/core/xmlhttprequest_api.php 2007-08-07 10:47:13.000000000 -0400 +++ mantis-tagging/core/xmlhttprequest_api.php 2007-08-16 13:48:44.000000000 -0400 @@ -31,6 +31,15 @@ echo ''; } + function xmlhttprequest_user_combobox() { + $f_user_id = gpc_get_int( 'user_id' ); + $f_user_access = gpc_get_int( 'access_level' ); + + echo ''; + } + # --------------- # Echos a serialized list of platforms starting with the prefix specified in the $_POST function xmlhttprequest_platform_get_with_prefix() { diff -urN --exclude=CVS --exclude=.svn --exclude=.swp --exclude='.git*' --exclude=config_inc.php mantis-cvs/javascript/common.js mantis-tagging/javascript/common.js --- mantis-cvs/javascript/common.js 2007-08-14 10:12:43.000000000 -0400 +++ mantis-tagging/javascript/common.js 2007-08-16 14:57:40.000000000 -0400 @@ -163,3 +163,16 @@ setDisplay( idTag, (document.getElementById(idTag).style.display == 'none')?1:0 ); } +/* Tag functionality */ +function tag_string_append( p_string ) { + t_tag_separator = document.getElementById('tag_separator').value; + t_tag_string = document.getElementById('tag_string'); + t_tag_select = document.getElementById('tag_select'); + if ( t_tag_string.value != '' ) { + t_tag_string.value = t_tag_string.value + t_tag_separator + p_string; + } else { + t_tag_string.value = t_tag_string.value + p_string; + } + t_tag_select.selectedIndex=0; +} + diff -urN --exclude=CVS --exclude=.svn --exclude=.swp --exclude='.git*' --exclude=config_inc.php mantis-cvs/lang/strings_english.txt mantis-tagging/lang/strings_english.txt --- mantis-cvs/lang/strings_english.txt 2007-08-16 13:24:00.000000000 -0400 +++ mantis-tagging/lang/strings_english.txt 2007-08-20 09:28:47.000000000 -0400 @@ -275,6 +275,11 @@ $MANTIS_ERROR[ERROR_USER_CHANGE_LAST_ADMIN] = 'You cannot change the access level of the only ADMINISTRATOR in the system.'; $MANTIS_ERROR[ERROR_PAGE_REDIRECTION] = 'Page redirection error, ensure that there are no spaces outside the PHP block (<?php ?>) in config_inc.php or custom_*.php files.'; $MANTIS_ERROR[ERROR_TWITTER_NO_CURL_EXT] = 'Twitter integration requires PHP CURL extension which is not installed.'; +$MANTIS_ERROR[ERROR_TAG_NOT_FOUND] = 'Could not find a tag with that name.'; +$MANTIS_ERROR[ERROR_TAG_DUPLICATE] = 'A tag already exists with that name.'; +$MANTIS_ERROR[ERROR_TAG_NAME_INVALID] = 'That tag name is invalid.'; +$MANTIS_ERROR[ERROR_TAG_NOT_ATTACHED] = 'That tag is not attached to that bug.'; +$MANTIS_ERROR[ERROR_TAG_ALREADY_ATTACHED] = 'That tag already attached to that bug.'; $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.'; @@ -1347,6 +1352,38 @@ # wiki related strings $s_wiki = 'Wiki'; +# Tagging +$s_tags = 'Tags'; +$s_tag_details = 'Tag Details: %s'; +$s_tag_id = 'Tag ID'; +$s_tag_name = 'Name'; +$s_tag_creator = 'Creator'; +$s_tag_created = 'Date Created'; +$s_tag_updated = 'Last Updated'; +$s_tag_description = 'Tag Description'; +$s_tag_statistics = 'Usage Statistics'; +$s_tag_update = 'Update Tag: %s'; +$s_tag_update_return = 'Back to Tag'; +$s_tag_update_button = 'Update Tag'; +$s_tag_delete_button = 'Delete Tag'; +$s_tag_delete_message = 'Are you sure you wish to delete this tag?'; +$s_tag_existing = 'Existing tags'; +$s_tag_none_attached = 'No tags attached.'; +$s_tag_attach = 'Attach'; +$s_tag_attach_long = 'Attach Tags'; +$s_tag_attach_failed = 'Tag attachment failed.'; +$s_tag_detach = 'Detach %s'; +$s_tag_separate_by = "(Separate by '%s')"; +$s_tag_invalid_name = 'Invalid tag name.'; +$s_tag_create_denied = 'Create permission denied.'; +$s_tag_filter_default = 'Attached Issues (%s)'; +$s_tag_history_attached = 'Tag Attached'; +$s_tag_history_detached = 'Tag Detached'; +$s_tag_history_renamed = 'Tag Renamed'; +$s_tag_related = 'Related Tags'; +$s_tag_related_issues = 'Shared Issues (%s)'; +$s_tag_stats_attached = 'Issues attached: %s'; + # Time Tracking $s_time_tracking_billing_link = 'Billing'; $s_time_tracking = 'Time tracking'; diff -urN --exclude=CVS --exclude=.svn --exclude=.swp --exclude='.git*' --exclude=config_inc.php mantis-cvs/tag_attach.php mantis-tagging/tag_attach.php --- mantis-cvs/tag_attach.php 1969-12-31 19:00:00.000000000 -0500 +++ mantis-tagging/tag_attach.php 2007-08-20 08:43:02.000000000 -0400 @@ -0,0 +1,108 @@ + 0 ) { + html_page_top1( lang_get( 'tag_attach_long' ).' '.bug_format_summary( $f_bug_id, SUMMARY_CAPTION ) ); + html_page_top2(); +?> +
+ + + + + +'; + if ( -1 == $t_tag_row['id'] ) { + echo ''; + } elseif ( -2 == $t_tag_row['id'] ) { + echo ''; + } + echo ''; + + if ( "" != $t_tag_string ) { + $t_tag_string .= config_get( 'tag_separator' ); + } + $t_tag_string .= $t_tag_row['name']; + } +?> + + > + + + +
',lang_get( 'tag_invalid_name' ),'',lang_get( 'tag_create_denied' ),'',$t_tag_row['name'],'
+ +
+ + +
+
+ + + + + + + + + + + + + + + + + +> + + + + + + + + + + + + + +> + + + + + + + + + +
+ + + + +
name="name" value=""/>'; + print_user_option_list( $t_tag_row['user_id'], ALL_PROJECTS, config_get( 'tag_create_threshold' ) ); + echo ''; + } + } else { + echo user_get_name($t_tag_row['user_id']); + } + ?>
+ +
+ type="submit" class="button" value="" /> +
+
+ + diff -urN --exclude=CVS --exclude=.svn --exclude=.swp --exclude='.git*' --exclude=config_inc.php mantis-cvs/tag_update.php mantis-tagging/tag_update.php --- mantis-cvs/tag_update.php 1969-12-31 19:00:00.000000000 -0500 +++ mantis-tagging/tag_update.php 2007-08-16 13:48:44.000000000 -0400 @@ -0,0 +1,55 @@ + + +
+ + + + + + + + + + + + + + + + + +> + + + + + + + + + + + + + +> + + + + + +'; + echo ''; + + $i = 0; + foreach( $t_tags_related as $t_tag ) { + $t_name = string_display_line( $t_tag['name'] ); + $t_description = string_display_line( $t_tag['description'] ); + $t_count = $t_tag['count']; + + echo ( $i > 0 ? '' : '' ); + echo "\n"; + echo ''; + + $i++; + } + } +?> + + + + + + +
+ + + + +
',lang_get( 'tag_related' ),'
$t_name'; + print_bracket_link( 'search.php?hide_status_id=90&tag_string='.urlencode("+$t_tag_row[name]".config_get('tag_separator')."+$t_name"), sprintf( lang_get( 'tag_related_issues' ), $t_tag['count'] ) ); + echo '
+ +
+ diff -urN --exclude=CVS --exclude=.svn --exclude=.swp --exclude='.git*' --exclude=config_inc.php mantis-cvs/view_all_set.php mantis-tagging/view_all_set.php --- mantis-cvs/view_all_set.php 2007-08-07 10:47:14.000000000 -0400 +++ mantis-tagging/view_all_set.php 2007-08-17 22:43:15.000000000 -0400 @@ -203,6 +203,9 @@ $f_do_filter_by_date = gpc_get_bool( 'do_filter_by_date' ); $f_view_state = gpc_get_int( 'view_state', META_FILTER_ANY ); + $f_tag_string = gpc_get_string( 'tag_string', '' ); + $f_tag_select = gpc_get_int( 'tag_select', '0' ); + $t_custom_fields = custom_field_get_ids(); # @@@ (thraxisp) This should really be the linked ids, but we don't know the project $f_custom_fields_data = array(); if ( is_array( $t_custom_fields ) && ( sizeof( $t_custom_fields ) > 0 ) ) { @@ -414,6 +417,8 @@ $t_setting_arr['platform'] = $f_platform; $t_setting_arr['os'] = $f_os; $t_setting_arr['os_build'] = $f_os_build; + $t_setting_arr['tag_string'] = $f_tag_string; + $t_setting_arr['tag_select'] = $f_tag_select; break; # Set the sort order and direction case '2': diff -urN --exclude=CVS --exclude=.svn --exclude=.swp --exclude='.git*' --exclude=config_inc.php mantis-cvs/view_filters_page.php mantis-tagging/view_filters_page.php --- mantis-cvs/view_filters_page.php 2007-08-07 10:47:14.000000000 -0400 +++ mantis-tagging/view_filters_page.php 2007-08-17 22:21:01.000000000 -0400 @@ -16,6 +16,7 @@ require_once( $t_core_path.'bug_api.php' ); require_once( $t_core_path.'string_api.php' ); require_once( $t_core_path.'date_api.php' ); + require_once( $t_core_path.'tag_api.php' ); auth_ensure_user_authenticated(); @@ -406,7 +407,8 @@ - + + @@ -414,13 +416,12 @@ - + -