From 2e2335841909ef0f25c2616f8b34ed152d726a8e Mon Sep 17 00:00:00 2001 From: Dominik Blunk Date: Mon, 12 Sep 2011 15:49:06 +0200 Subject: [PATCH] Implemented project specific tags @see http://www.mantisbt.org/bugs/view.php?id=9716 To enable this feature run a Mantis update (new column "project_id" in tag table is required) and set the config value $g_tag_project_specific_enabled to ON (defaults to OFF) If project specific tags are enabled the following happens: a) All existing tags will be considered as "global" (tags are saved with project_id = 0). Every "global" tag may be used in all projects. b) Newly created tags are related to the current project (project of the bug precisely). These tags are only available within the same project scope. c) No special handling of "parent projects" - a parent project is equal to a child project. Either tags for the parent project exists or not - no usage of "collected child project tags" d) The tag - project relation may be edited in the tag management section. The tag overview shows only "global" tags and tags for the current project. e) The filter shows only "global" tags and tags for the current project. Therefore filtering issues for specific tag(s) over multiple projects works only with "global" tags. f) When switching $g_tag_project_specific_enabled between ON and OFF the existing tags will keep its project relation. New tags will always be "global" for "all projects". This makes sure that existing "project specific" tags are not unintentionally "globally" available Other improvements: - Added auth_reauthenticate() to the manage_tags_page.php - Added the print_manage_menu() to tag_view_page.php and tag_update_page.php to improve usability (navigation back to tags overview after viewing/editing a tag) --- admin/schema.php | 4 ++ bug_actiongroup_attach_tags_inc.php | 3 +- config_defaults_inc.php | 7 +++ core/tag_api.php | 97 ++++++++++++++++++++++++----------- manage_tags_page.php | 27 ++++++++-- tag_attach.php | 6 ++- tag_create.php | 9 +++- tag_update.php | 8 +++- tag_update_page.php | 24 +++++++-- tag_view_page.php | 22 +++++--- 10 files changed, 150 insertions(+), 57 deletions(-) diff --git a/admin/schema.php b/admin/schema.php index d2b6940..505dd9b 100644 --- a/admin/schema.php +++ b/admin/schema.php @@ -608,3 +608,7 @@ $upgrade[] = Array( 'CreateIndexSQL', Array( 'idx_tag_name', db_get_table( 'mant $upgrade[] = Array( 'CreateIndexSQL', Array( 'idx_bug_tag_tag_id', db_get_table( 'mantis_bug_tag_table' ), 'tag_id' ) ); $upgrade[] = Array( 'CreateIndexSQL', Array( 'idx_email_id', db_get_table( 'mantis_email_table' ), 'email_id', array( 'DROP' ) ), Array( 'db_index_exists', Array( db_get_table( 'mantis_email_table' ), 'idx_email_id') ) ); $upgrade[] = Array( 'UpdateFunction', 'correct_multiselect_custom_fields_db_format' ); + +/* 190 */ +$upgrade[] = Array( 'AddColumnSQL', Array( db_get_table( 'mantis_tag_table' ), "project_id I UNSIGNED NOTNULL DEFAULT '0'" ) ); +$upgrade[] = Array( 'CreateIndexSQL', Array( 'idx_project_id', db_get_table( 'mantis_tag_table' ), 'project_id' ) ); diff --git a/bug_actiongroup_attach_tags_inc.php b/bug_actiongroup_attach_tags_inc.php index 3105acf..fca39b6 100644 --- a/bug_actiongroup_attach_tags_inc.php +++ b/bug_actiongroup_attach_tags_inc.php @@ -108,9 +108,10 @@ global $g_action_attach_tags_attach, $g_action_attach_tags_create; $t_user_id = auth_get_current_user_id(); + $t_bug = bug_get( $p_bug_id ); foreach( $g_action_attach_tags_create as $t_tag_row ) { - $t_tag_row['id'] = tag_create( $t_tag_row['name'], $t_user_id ); + $t_tag_row['id'] = tag_create( $t_tag_row['name'], $t_user_id, '', $t_bug->project_id ); $g_action_attach_tags_attach[] = $t_tag_row; } $g_action_attach_tags_create = array(); diff --git a/config_defaults_inc.php b/config_defaults_inc.php index 59b4764..2261a05 100644 --- a/config_defaults_inc.php +++ b/config_defaults_inc.php @@ -3506,6 +3506,13 @@ /*************** * Bug Tagging * ***************/ + + /** + * When enabled tags will be project specific unless explicitely set to "global" + * in the manage tags section + * @global int $g_tag_project_specific_enabled + */ + $g_tag_project_specific_enabled = OFF; /** * String that will separate tags as entered for input diff --git a/core/tag_api.php b/core/tag_api.php index fd31424..6a1b224 100644 --- a/core/tag_api.php +++ b/core/tag_api.php @@ -65,14 +65,15 @@ function tag_ensure_exists( $p_tag_id ) { * Determine if a given name is unique (not already used). * Uses a case-insensitive search of the database for existing tags with the same name. * @param string Tag name + * @param integer Project ID * @return boolean True if name is unique */ -function tag_is_unique( $p_name ) { +function tag_is_unique( $p_name, $p_project_id = 0 ) { $c_name = trim( $p_name ); $t_tag_table = db_get_table( 'mantis_tag_table' ); - $query = 'SELECT id FROM ' . $t_tag_table . ' WHERE ' . db_helper_like( 'name' ); - $result = db_query_bound( $query, Array( $c_name ) ); + $query = 'SELECT id FROM ' . $t_tag_table . ' WHERE ' . db_helper_like( 'name' ) . " AND project_id = " . db_param(); + $result = db_query_bound( $query, Array( $c_name, $p_project_id ) ); return db_num_rows( $result ) == 0; } @@ -80,9 +81,10 @@ function tag_is_unique( $p_name ) { /** * Ensure that a name is unique. * @param string Tag name + * @param integer Project ID */ -function tag_ensure_unique( $p_name ) { - if( !tag_is_unique( $p_name ) ) { +function tag_ensure_unique( $p_name, $p_project_id = 0 ) { + if( !tag_is_unique( $p_name, $p_project_id ) ) { trigger_error( ERROR_TAG_DUPLICATE, ERROR ); } } @@ -136,9 +138,10 @@ function tag_cmp_name( $p_tag1, $p_tag2 ) { * id = -1 and the tag name, and if the name is invalid, a row is returned with * id = -2 and the tag name. The resulting array is then sorted by tag name. * @param string Input string to parse + * @param int Project ID * @return array Rows of tags parsed from input string */ -function tag_parse_string( $p_string ) { +function tag_parse_string( $p_string, $p_project_id = 0 ) { $t_tags = array(); $t_strings = explode( config_get( 'tag_separator' ), $p_string ); @@ -149,19 +152,27 @@ function tag_parse_string( $p_string ) { } $t_matches = array(); - $t_tag_row = tag_get_by_name( $t_name ); + $t_tag_row = tag_get_by_name( $t_name, $p_project_id ); if( $t_tag_row !== false ) { $t_tags[] = $t_tag_row; } else { - if( tag_name_is_valid( $t_name, $t_matches ) ) { - $t_id = -1; + if ( config_get( 'tag_project_specific_enabled' ) && $p_project_id > 0 ) { + // check if "global" tag exists + $t_tag_row = tag_get_by_name( $t_name, 0 ); + } + if( $t_tag_row !== false ) { + $t_tags[] = $t_tag_row; } else { - $t_id = -2; + if( tag_name_is_valid( $t_name, $t_matches ) ) { + $t_id = -1; + } else { + $t_id = -2; + } + $t_tags[] = array( + 'id' => $t_id, + 'name' => $t_name, + ); } - $t_tags[] = array( - 'id' => $t_id, - 'name' => $t_name, - ); } } usort( $t_tags, 'tag_cmp_name' ); @@ -189,6 +200,10 @@ function tag_parse_filters( $p_string ) { if( !is_blank( $t_name ) && tag_name_is_valid( $t_name, $t_matches, $t_prefix ) ) { $t_tag_row = tag_get_by_name( $t_matches[1] ); + if( $t_tag_row === false && config_get( 'tag_project_specific_enabled' ) ) { + // no "global" tag found -> check project specific tag + $t_tag_row = tag_get_by_name( $t_matches[1], helper_get_current_project() ); + } if( $t_tag_row !== false ) { $t_filter = utf8_substr( $t_name, 0, 1 ); @@ -238,14 +253,17 @@ function tag_get( $p_tag_id ) { /** * Return a tag row for the given name. * @param string Tag name + * @param int Project ID * @return Tag row */ -function tag_get_by_name( $p_name ) { +function tag_get_by_name( $p_name, $p_project_id = 0 ) { $t_tag_table = db_get_table( 'mantis_tag_table' ); + $t_params = array( $p_name, $p_project_id ); $query = "SELECT * FROM $t_tag_table - WHERE " . db_helper_like( 'name' ); - $result = db_query_bound( $query, Array( $p_name ) ); + WHERE " . db_helper_like( 'name' ) ." + and project_id = " . db_param(); + $result = db_query_bound( $query, $t_params ); if( 0 == db_num_rows( $result ) ) { return false; @@ -279,13 +297,14 @@ function tag_get_field( $p_tag_id, $p_field_name ) { * @param string Tag name * @param integer User ID * @param string Description + * @param integer Project ID * @return integer Tag ID */ -function tag_create( $p_name, $p_user_id = null, $p_description = '' ) { +function tag_create( $p_name, $p_user_id = null, $p_description = '', $p_project_id = 0 ) { access_ensure_global_level( config_get( 'tag_create_threshold' ) ); tag_ensure_name_is_valid( $p_name ); - tag_ensure_unique( $p_name ); + tag_ensure_unique( $p_name, $p_project_id ); if( null == $p_user_id ) { $p_used_id = auth_get_current_user_id(); @@ -294,12 +313,17 @@ function tag_create( $p_name, $p_user_id = null, $p_description = '' ) { } $c_user_id = db_prepare_int( $p_user_id ); + $c_project_id = db_prepare_int( $p_project_id ); + if ( !config_get( 'tag_project_specific_enabled' ) ) { + $c_project_id = 0; + } $c_date_created = db_now(); $t_tag_table = db_get_table( 'mantis_tag_table' ); $query = "INSERT INTO $t_tag_table ( user_id, + project_id, name, description, date_created, @@ -310,10 +334,11 @@ function tag_create( $p_name, $p_user_id = null, $p_description = '' ) { " . db_param() . ", " . db_param() . ", " . db_param() . ", + " . db_param() . ", " . db_param() . " )"; - db_query_bound( $query, Array( $c_user_id, trim( $p_name ), trim( $p_description ), $c_date_created, $c_date_created ) ); + db_query_bound( $query, Array( $c_user_id, $c_project_id, trim( $p_name ), trim( $p_description ), $c_date_created, $c_date_created ) ); return db_insert_id( $t_tag_table ); } @@ -322,9 +347,10 @@ function tag_create( $p_name, $p_user_id = null, $p_description = '' ) { * @param integer Tag ID * @param string Tag name * @param integer User ID + * @param integer Project ID * @param string Description */ -function tag_update( $p_tag_id, $p_name, $p_user_id, $p_description ) { +function tag_update( $p_tag_id, $p_name, $p_user_id, $p_description, $p_project_id = 0 ) { user_ensure_exists( $p_user_id ); if( auth_get_current_user_id() == tag_get_field( $p_tag_id, 'user_id' ) ) { @@ -352,11 +378,19 @@ function tag_update( $p_tag_id, $p_name, $p_user_id, $p_description ) { $query = "UPDATE $t_tag_table SET user_id=" . db_param() . ", - name=" . db_param() . ", - description=" . db_param() . ", + name=" . db_param() . ","; + if ( $p_project_id >= 0 ) { + $query .= " project_id=" . db_param() . ","; + } + $query .= " description=" . db_param() . ", date_updated=" . db_param() . " WHERE id=" . db_param(); - db_query_bound( $query, Array( (int)$p_user_id, $p_name, $p_description, $c_date_updated, $c_tag_id ) ); + if ( $p_project_id >= 0 ) { + db_query_bound( $query, Array( (int)$p_user_id, $p_name, $p_project_id, $p_description, $c_date_updated, $c_tag_id ) ); + } + else { + db_query_bound( $query, Array( (int)$p_user_id, $p_name, $p_description, $c_date_updated, $c_tag_id ) ); + } if( $t_rename ) { $t_bugs = tag_get_bugs_attached( $p_tag_id ); @@ -408,12 +442,13 @@ function tag_get_candidates_for_bug( $p_bug_id ) { $t_params = array(); if ( 0 != $p_bug_id ) { $t_bug_tag_table = db_get_table( 'mantis_bug_tag_table' ); - + $t_bug = bug_get( $p_bug_id ); + $t_params[] = $p_bug_id; if ( db_is_mssql() ) { - $t_params[] = $p_bug_id; $query = "SELECT t.id FROM $t_tag_table t LEFT JOIN $t_bug_tag_table b ON t.id=b.tag_id - WHERE b.bug_id IS NULL OR b.bug_id != " . db_param(); + WHERE (b.bug_id IS NULL OR b.bug_id != " . db_param() .") + AND project_id IN (0, " . $t_bug->project_id . ")"; $result = db_query_bound( $query, $t_params ); $t_subquery_results = array(); @@ -426,12 +461,12 @@ function tag_get_candidates_for_bug( $p_bug_id ) { $query = "SELECT id, name, description FROM $t_tag_table WHERE id IN ( SELECT t.id FROM $t_tag_table t LEFT JOIN $t_bug_tag_table b ON t.id=b.tag_id - WHERE b.bug_id IS NULL OR b.bug_id != " . db_param() . - ')'; + WHERE (b.bug_id IS NULL OR b.bug_id != " . db_param() . ") + AND project_id IN (0, " . $t_bug->project_id . ") + )"; } - $t_params[] = $p_bug_id; } else { - $query = 'SELECT id, name, description FROM ' . $t_tag_table; + $query = 'SELECT id, name, description FROM ' . $t_tag_table . " WHERE project_id IN (0, " . helper_get_current_project() . ")"; } $query .= ' ORDER BY name ASC '; diff --git a/manage_tags_page.php b/manage_tags_page.php index 0718160..f0908ad 100644 --- a/manage_tags_page.php +++ b/manage_tags_page.php @@ -32,6 +32,8 @@ require_once( 'tag_api.php' ); require_once( 'user_pref_api.php' ); require_once( 'form_api.php' ); +auth_reauthenticate(); + access_ensure_global_level( config_get( 'tag_edit_threshold' ) ); compress_enable(); @@ -78,11 +80,10 @@ echo ''; $t_where_params = array(); -if ( $f_filter === 'ALL' ) { - $t_where = ''; -} else { +$t_where = "WHERE project_id in (0, " . helper_get_current_project() . ")"; +if ( $f_filter !== 'ALL' ) { $t_where_params[] = $f_filter . '%'; - $t_where = 'WHERE ' . db_helper_like( 'name' ); + $t_where .= ' AND ' . db_helper_like( 'name' ); } # Set the number of Tags per page. @@ -141,7 +142,8 @@ $t_result = db_query_bound( $t_query, $t_where_params, $t_per_page, $t_offset ); - + + @@ -157,6 +159,7 @@ foreach ( $t_result as $t_tag_row ) { + @@ -204,7 +207,19 @@ foreach ( $t_result as $t_tag_row ) { - + + + + + + + + + + + diff --git a/tag_attach.php b/tag_attach.php index 14086f7..09b908b 100644 --- a/tag_attach.php +++ b/tag_attach.php @@ -32,6 +32,8 @@ form_security_validate( 'tag_attach' ); $f_bug_id = gpc_get_int( 'bug_id' ); + $t_bug = bug_get( $f_bug_id ); + $t_project_id = $t_bug->project_id; $f_tag_select = gpc_get_int( 'tag_select' ); $f_tag_string = gpc_get_string( 'tag_string' ); @@ -43,7 +45,7 @@ * to the APIs. This is to allow other clients of the API to support such * functionality. The access level checks should also be moved to the API. */ - $t_tags = tag_parse_string( $f_tag_string ); + $t_tags = tag_parse_string( $f_tag_string, $t_project_id ); $t_can_create = access_has_global_level( config_get( 'tag_create_threshold' ) ); $t_tags_create = array(); @@ -117,7 +119,7 @@ // end failed to attach tag } else { foreach( $t_tags_create as $t_tag_row ) { - $t_tag_row['id'] = tag_create( $t_tag_row['name'], $t_user_id ); + $t_tag_row['id'] = tag_create( $t_tag_row['name'], $t_user_id, '', $t_project_id ); $t_tags_attach[] = $t_tag_row; } diff --git a/tag_create.php b/tag_create.php index fa9662f..83c62fe 100644 --- a/tag_create.php +++ b/tag_create.php @@ -32,6 +32,13 @@ form_security_validate( 'tag_create' ); $f_tag_name = gpc_get_string( 'name' ); $f_tag_description = gpc_get_string( 'description' ); +if ( config_get( 'tag_project_specific_enabled' ) ) { + $f_project_id = gpc_get_int( 'project_id' ); +} +else { + $f_project_id = 0; +} + $t_tag_user = auth_get_current_user_id(); @@ -39,7 +46,7 @@ if ( !is_null( $f_tag_name )) { $t_tags = tag_parse_string( $f_tag_name ); foreach ( $t_tags as $t_tag_row ) { if ( -1 == $t_tag_row['id'] ) { - tag_create( $t_tag_row['name'], $t_tag_user, $f_tag_description ); + tag_create( $t_tag_row['name'], $t_tag_user, $f_tag_description, $f_project_id ); } } } diff --git a/tag_update.php b/tag_update.php index 1252f89..310b40c 100644 --- a/tag_update.php +++ b/tag_update.php @@ -35,6 +35,12 @@ $f_tag_id = gpc_get_int( 'tag_id' ); $t_tag_row = tag_get( $f_tag_id ); + if ( config_get( 'tag_project_specific_enabled' ) ) { + $f_project_id = gpc_get_int( 'project_id' ); + } + else { + $f_project_id = -1; + } if ( !( access_has_global_level( config_get( 'tag_edit_threshold' ) ) || ( auth_get_current_user_id() == $t_tag_row['user_id'] ) @@ -65,7 +71,7 @@ $t_update = true; } - tag_update( $f_tag_id, $f_new_name, $f_new_user_id, $f_new_description ); + tag_update( $f_tag_id, $f_new_name, $f_new_user_id, $f_new_description, $f_project_id ); form_security_purge( 'tag_update' ); diff --git a/tag_update_page.php b/tag_update_page.php index 69de115..323aac6 100644 --- a/tag_update_page.php +++ b/tag_update_page.php @@ -49,6 +49,8 @@ } html_page_top( sprintf( lang_get( 'tag_update' ), $t_name ) ); + + print_manage_menu( '' ); ?>
@@ -69,16 +71,26 @@ - + - - - + + + + > name="name" value=""/> + + + + + - + > - + diff --git a/tag_view_page.php b/tag_view_page.php index 1eb3e7f..7a811a6 100644 --- a/tag_view_page.php +++ b/tag_view_page.php @@ -39,6 +39,8 @@ $t_description = string_display( $t_tag_row['description'] ); html_page_top( sprintf( lang_get( 'tag_details' ), $t_name ) ); + + print_manage_menu( '' ); ?>
@@ -57,16 +59,18 @@ - - - - - + + + + + + > + @@ -74,13 +78,13 @@ - + > - + @@ -98,7 +102,7 @@ echo ( $i > 0 ? '' : '' ); echo "$t_name\n"; - echo ''; + echo ''; print_bracket_link( 'search.php?tag_string='.urlencode("+$t_tag_row[name]".config_get('tag_separator')."+$t_name"), sprintf( lang_get( 'tag_related_issues' ), $t_tag['count'] ) ); echo ''; @@ -109,7 +113,7 @@ - +