diff --git a/admin/schema.php b/admin/schema.php index 6aefc72..c8423ba 100644 --- a/admin/schema.php +++ b/admin/schema.php @@ -382,7 +382,7 @@ $upgrade[] = Array( 'CreateTableSQL', Array( db_get_table( 'mantis_category_tabl $upgrade[] = Array( 'CreateIndexSQL', Array( 'idx_category_project_name', db_get_table( 'mantis_category_table' ), 'project_id, name', array( 'UNIQUE' ) ) ); $upgrade[] = Array( 'InsertData', Array( db_get_table( 'mantis_category_table' ), " ( project_id, user_id, name, status ) VALUES - ( '0', '0', 'None', '0' ) " ) ); + ( '0', '0', 'General', '0' ) " ) ); $upgrade[] = Array( 'AddColumnSQL', Array( db_get_table( 'mantis_bug_table' ), "category_id I UNSIGNED NOTNULL DEFAULT '1'" ) ); $upgrade[] = Array( 'UpdateFunction', "category_migrate" ); $upgrade[] = Array( 'DropColumnSQL', Array( db_get_table( 'mantis_bug_table' ), "category" ) ); diff --git a/bug_update_advanced_page.php b/bug_update_advanced_page.php index 03ed16d..8f7768f 100644 --- a/bug_update_advanced_page.php +++ b/bug_update_advanced_page.php @@ -115,9 +115,6 @@ - project_id, 'name' ) . "] "; - } ?> diff --git a/bug_update_page.php b/bug_update_page.php index 151b1bc..abf3501 100644 --- a/bug_update_page.php +++ b/bug_update_page.php @@ -118,9 +118,6 @@ - project_id, 'name' ) . "] "; - } ?> diff --git a/core/category_api.php b/core/category_api.php index cabde42..a768fae 100644 --- a/core/category_api.php +++ b/core/category_api.php @@ -149,7 +149,7 @@ # -------------------- # Remove a category from the project - function category_remove( $p_category_id, $p_new_category_id = 1 ) { + function category_remove( $p_category_id, $p_new_category_id = 0 ) { $t_category_row = category_get_row( $p_category_id ); $c_category_id = db_prepare_int( $p_category_id ); @@ -172,7 +172,6 @@ $t_result = db_query_bound( $query, array( $c_category_id ) ); while ( $t_bug_row = db_fetch_array( $t_result ) ) { - var_dump( $t_bug_row ); history_log_event_direct( $t_bug_row['id'], 'category', $t_category_row['name'], category_full_name( $p_new_category_id, false ) ); } @@ -188,7 +187,7 @@ # -------------------- # Remove all categories associated with a project - function category_remove_all( $p_project_id, $p_new_category_id = 1 ) { + function category_remove_all( $p_project_id, $p_new_category_id = 0 ) { $c_project_id = db_prepare_int( $p_project_id ); $c_new_category_id = db_prepare_int( $p_new_category_id ); @@ -258,19 +257,55 @@ } # -------------------- - # Return all categories for the specified project id - function category_get_all_rows( $p_project_id ) { + # Sort categories based on what project they're in. + # Call beforehand with a single parameter to set a 'preferred' project. + function category_sort_rows_by_project( $p_category1, $p_category2=null ) { + static $p_project_id=null; + if ( is_null( $p_category2 ) ) { # Set a target project + $p_project_id = $p_category1; + return; + } + + if ( !is_null( $p_project_id ) ) { + if ( $p_category1['project_id'] == $p_project_id && + $p_category2['project_id'] != $p_project_id ) { + return -1; + } + if ( $p_category1['project_id'] != $p_project_id && + $p_category2['project_id'] == $p_project_id ) { + return 1; + } + } + + $t_proj_cmp = strcasecmp( $p_category1['project_name'], $p_category2['project_name'] ); + if ( $t_proj_cmp != 0 ) { + return $t_proj_cmp; + } + + return strcasecmp( $p_category1['name'], $p_category2['name'] ); + } + # -------------------- + # Return all categories for the specified project id. + # Obeys project hierarchies and such. + function category_get_all_rows( $p_project_id, $p_inherit=true, $p_sort_by_project=false ) { global $g_category_cache; + project_hierarchy_cache(); + $c_project_id = db_prepare_int( $p_project_id ); $t_category_table = db_get_table( 'mantis_category_table' ); $t_project_table = db_get_table( 'mantis_project_table' ); - $t_project_where = helper_project_specific_where( $c_project_id ); + if ( $p_inherit ) { + $t_project_ids = project_hierarchy_inheritance( $p_project_id ); + $t_project_where = ' project_id IN ( ' . implode( ', ', $t_project_ids ) . ' ) '; + } else { + $t_project_where = ' project_id=' . $p_project_id . ' '; + } $query = "SELECT c.*, p.name AS project_name FROM $t_category_table AS c - JOIN $t_project_table AS p + LEFT JOIN $t_project_table AS p ON c.project_id=p.id WHERE $t_project_where ORDER BY c.name "; @@ -284,6 +319,12 @@ $g_category_cache[$row['id']] = $row; } + if ( $p_sort_by_project ) { + category_sort_rows_by_project( $p_project_id ); + usort( $rows, 'category_sort_rows_by_project' ); + category_sort_rows_by_project( null ); + } + return $rows; } @@ -311,7 +352,7 @@ $t_row = category_get_row( $p_category_id ); $t_project_id = $t_row['project_id']; - if ( $p_show_project && ALL_PROJECTS != $t_project_id ) { + if ( $p_show_project ) { return '[' . project_get_name( $t_project_id ) . '] ' . $t_row['name']; } diff --git a/core/print_api.php b/core/print_api.php index 536a644..894e9c4 100644 --- a/core/print_api.php +++ b/core/print_api.php @@ -696,29 +696,17 @@ $t_project_table = db_get_table( 'mantis_project_table' ); if ( null === $p_project_id ) { - $c_project_id = helper_get_current_project(); + $t_project_id = helper_get_current_project(); } else { - $c_project_id = db_prepare_int( $p_project_id ); + $t_project_id = $p_project_id; } - $t_project_where = helper_project_specific_where( $c_project_id ); - - # grab all categories in the project category table - $cat_arr = array(); - $query = "SELECT id,name FROM $t_category_table - WHERE $t_project_where - ORDER BY name"; - $result = db_query( $query ); - - while ( $row = db_fetch_array( $result ) ) { - $cat_arr[$row['id']] = $row['name']; - } - asort($cat_arr); + $cat_arr = category_get_all_rows( $t_project_id, true, !is_null( $p_project_id ) ); - foreach( $cat_arr as $t_category_id => $t_name ) { - PRINT ""; + foreach( $cat_arr as $t_category_row ) { + $t_category_id = $t_category_row['id']; + echo "'; } } # -------------------- @@ -742,7 +730,16 @@ $c_project_id = db_prepare_int( $p_project_id ); } - $t_project_where = helper_project_specific_where( $c_project_id ); + project_hierarchy_cache(); + $t_project_ids = project_hierarchy_inheritance( $c_project_id ); + + $t_subproject_ids = array(); + foreach ( $t_project_ids as $t_project_id ) { + $t_subproject_ids = array_merge( $t_subproject_ids, current_user_get_all_accessible_subprojects( $t_project_id ) ); + } + + $t_project_ids = array_merge( $t_project_ids, $t_subproject_ids ); + $t_project_where = ' project_id IN ( ' . implode( ', ', $t_project_ids ) . ' ) '; # grab all categories in the project category table $cat_arr = array(); diff --git a/core/project_api.php b/core/project_api.php index 66476a8..194ee44 100644 --- a/core/project_api.php +++ b/core/project_api.php @@ -211,7 +211,7 @@ # -------------------- # Create a new project - function project_create( $p_name, $p_description, $p_status, $p_view_state = VS_PUBLIC, $p_file_path = '', $p_enabled = true ) { + function project_create( $p_name, $p_description, $p_status, $p_view_state = VS_PUBLIC, $p_file_path = '', $p_enabled = true, $p_inherit_global = true ) { # Make sure file path has trailing slash $p_file_path = terminate_directory_path( $p_file_path ); @@ -221,6 +221,7 @@ $c_view_state = db_prepare_int( $p_view_state ); $c_file_path = db_prepare_string( $p_file_path ); $c_enabled = db_prepare_bool( $p_enabled ); + $c_inherit_global = db_prepare_bool( $p_inherit_global ); if ( is_blank( $p_name ) ) { trigger_error( ERROR_PROJECT_NAME_INVALID, ERROR ); @@ -235,9 +236,9 @@ $t_project_table = db_get_table( 'mantis_project_table' ); $query = "INSERT INTO $t_project_table - ( name, status, enabled, view_state, file_path, description ) + ( name, status, enabled, view_state, file_path, description, inherit_global ) VALUES - ( '$c_name', '$c_status', '$c_enabled', '$c_view_state', '$c_file_path', '$c_description' )"; + ( '$c_name', '$c_status', '$c_enabled', '$c_view_state', '$c_file_path', '$c_description', '$c_inherit_global' )"; db_query( $query ); @@ -302,7 +303,7 @@ # -------------------- # Update a project - function project_update( $p_project_id, $p_name, $p_description, $p_status, $p_view_state, $p_file_path, $p_enabled ) { + function project_update( $p_project_id, $p_name, $p_description, $p_status, $p_view_state, $p_file_path, $p_enabled, $p_inherit_global ) { # Make sure file path has trailing slash $p_file_path = terminate_directory_path( $p_file_path ); @@ -313,6 +314,7 @@ $c_view_state = db_prepare_int( $p_view_state ); $c_file_path = db_prepare_string( $p_file_path ); $c_enabled = db_prepare_bool( $p_enabled ); + $c_inherit_global = db_prepare_bool( $p_inherit_global ); if ( is_blank( $p_name ) ) { trigger_error( ERROR_PROJECT_NAME_INVALID, ERROR ); @@ -336,7 +338,8 @@ enabled='$c_enabled', view_state='$c_view_state', file_path='$c_file_path', - description='$c_description' + description='$c_description', + inherit_global='$c_inherit_global' WHERE id='$c_project_id'"; db_query( $query ); diff --git a/core/project_hierarchy_api.php b/core/project_hierarchy_api.php index accb345..1be60ac 100644 --- a/core/project_hierarchy_api.php +++ b/core/project_hierarchy_api.php @@ -26,7 +26,7 @@ $t_core_dir = dirname( __FILE__ ).DIRECTORY_SEPARATOR; # -------------------- - function project_hierarchy_add( $p_child_id, $p_parent_id ) { + function project_hierarchy_add( $p_child_id, $p_parent_id, $p_inherit_parent = true ) { if ( in_array( $p_parent_id, project_hierarchy_get_all_subprojects( $p_child_id ) ) ) { trigger_error( ERROR_PROJECT_RECURSIVE_HIERARCHY, ERROR ); } @@ -35,13 +35,28 @@ $c_child_id = db_prepare_int( $p_child_id ); $c_parent_id = db_prepare_int( $p_parent_id ); + $c_inherit_parent = db_prepare_bool( $p_inherit_parent ); $query = "INSERT INTO $t_project_hierarchy_table - ( child_id, parent_id ) + ( child_id, parent_id, inherit_parent ) VALUES - ( " . db_param(0) . ", " . db_param(1) . " )"; + ( " . db_param(0) . ', ' . db_param(1) . ', ' . db_param(2) . ' )'; - db_query_bound($query, Array( $c_child_id, $c_parent_id ) ); + db_query_bound($query, Array( $c_child_id, $c_parent_id, $c_inherit_parent ) ); + } + + function project_hierarchy_update( $p_child_id, $p_parent_id, $p_inherit_parent = true ) { + $t_project_hierarchy_table = db_get_table( 'mantis_project_hierarchy_table' ); + + $c_child_id = db_prepare_int( $p_child_id ); + $c_parent_id = db_prepare_int( $p_parent_id ); + $c_inherit_parent = db_prepare_bool( $p_inherit_parent ); + + $query = "UPDATE $t_project_hierarchy_table + SET inherit_parent=" . db_param(0) . ' + WHERE child_id=' . db_param(1) . ' + AND parent_id=' . db_param(2); + db_query_bound( $query, Array( $c_inherit_parent, $c_child_id, $c_parent_id ) ); } # -------------------- @@ -87,16 +102,21 @@ } $g_cache_project_hierarchy = null; + $g_cache_project_inheritance = null; # -------------------- function project_hierarchy_cache( $p_show_disabled = false ) { - global $g_cache_project_hierarchy; + global $g_cache_project_hierarchy, $g_cache_project_inheritance; + + if ( !is_null( $g_cache_project_hierarchy ) ) { + return; + } $t_project_table = db_get_table( 'mantis_project_table' ); $t_project_hierarchy_table = db_get_table( 'mantis_project_hierarchy_table' ); $t_enabled_clause = $p_show_disabled ? '1=1' : 'p.enabled = ' . db_param(0); - $query = "SELECT DISTINCT p.id, ph.parent_id, p.name + $query = "SELECT DISTINCT p.id, ph.parent_id, p.name, p.inherit_global, ph.inherit_parent FROM $t_project_table p LEFT JOIN $t_project_hierarchy_table ph ON ph.child_id = p.id @@ -107,6 +127,7 @@ $row_count = db_num_rows( $result ); $g_cache_project_hierarchy = array(); + $g_cache_project_inheritance = array(); for ( $i=0 ; $i < $row_count ; $i++ ){ $row = db_fetch_array( $result ); @@ -120,9 +141,57 @@ } else { $g_cache_project_hierarchy[ $row['parent_id'] ] = array( $row['id'] ); } + + if ( !isset( $g_cache_project_inheritance[ $row['id'] ] ) ) { + $g_cache_project_inheritance[ $row['id'] ] = array(); + } + + if ( $row['inherit_global'] && !isset( $g_cache_project_inheritance[ $row['id'] ][ ALL_PROJECTS ] ) ) { + $g_cache_project_inheritance[ $row['id'] ][] = ALL_PROJECTS; + } + if ( $row['inherit_parent'] && !isset( $g_cache_project_inheritance[ $row['id'] ][ $row['parent_id'] ] ) ) { + $g_cache_project_inheritance[ $row['id'] ][] = (int)$row['parent_id']; + } } } + # -------------------- + # Returns true if the child project inherits categories from the parent. + function project_hierarchy_inherit_parent( $p_child_id, $p_parent_id ) { + global $g_cache_project_inheritance; + + return in_array( $p_parent_id, $g_cache_project_inheritance[ $p_child_id ] ); + } + + # -------------------- + # Generate an array of project's the given project inherits from, + # including the original project in the result. + function project_hierarchy_inheritance( $p_project_id ) { + global $g_cache_project_inheritance; + + $t_project_ids = array( (int)$p_project_id ); + $t_lookup_ids = array( (int)$p_project_id ); + + while( count( $t_lookup_ids ) > 0 ) { + $t_project_id = array_shift( $t_lookup_ids ); + + if ( !isset( $g_cache_project_inheritance[ $t_project_id ] ) ) { + continue; + } + + foreach( $g_cache_project_inheritance[ $t_project_id ] as $t_parent_id ) { + if ( !in_array( $t_parent_id, $t_project_ids ) ) { + $t_project_ids[] = $t_parent_id; + + if ( !in_array( $t_lookup_ids, $t_project_ids ) ) { + $t_lookup_ids[] = $t_parent_id; + } + } + } + } + + return $t_project_ids; + } # -------------------- function project_hierarchy_get_subprojects( $p_project_id, $p_show_disabled = false ) { diff --git a/lang/strings_english.txt b/lang/strings_english.txt index 5c1a2dc..f791f53 100644 --- a/lang/strings_english.txt +++ b/lang/strings_english.txt @@ -99,7 +99,13 @@ $s_empty_password_sure_msg = 'The user has an empty password. Are you sure that $s_empty_password_button = 'Use Empty Password'; $s_reauthenticate_title = 'Authenticate'; $s_reauthenticate_message = 'You are visting a secure page, and your secure session has expired. Please authenticate yourself to continue.'; + $s_no_category = 'No Category'; +$s_global_categories = 'Global Categories'; +$s_inherit = 'Inherit Categories'; +$s_inherit_global = 'Inherit Global Categories'; +$s_inherit_parent = 'Inherit Parent Categories'; +$s_update_subproject_inheritance = 'Update Subproject Inheritance'; $s_duplicate_of = "duplicate of"; $s_has_duplicate = "has duplicate"; diff --git a/manage_proj_cat_add.php b/manage_proj_cat_add.php index 9675b08..92764b8 100644 --- a/manage_proj_cat_add.php +++ b/manage_proj_cat_add.php @@ -59,7 +59,11 @@ } } - $t_redirect_url = 'manage_proj_edit_page.php?project_id=' . $f_project_id; + if ( $f_project_id == ALL_PROJECTS ) { + $t_redirect_url = 'manage_proj_page.php'; + } else { + $t_redirect_url = 'manage_proj_edit_page.php?project_id=' . $f_project_id; + } print_header_redirect( $t_redirect_url ); ?> diff --git a/manage_proj_cat_copy.php b/manage_proj_cat_copy.php index f205c71..43e6b9f 100644 --- a/manage_proj_cat_copy.php +++ b/manage_proj_cat_copy.php @@ -57,5 +57,11 @@ } } - print_header_redirect( 'manage_proj_edit_page.php?project_id=' . $f_project_id ); + if ( $f_project_id == ALL_PROJECTS ) { + $t_redirect_url = 'manage_proj_page.php'; + } else { + $t_redirect_url = 'manage_proj_edit_page.php?project_id=' . $f_project_id; + } + + print_header_redirect( $t_redirect_url ); ?> diff --git a/manage_proj_cat_delete.php b/manage_proj_cat_delete.php index 934e7bf..971b8cd 100644 --- a/manage_proj_cat_delete.php +++ b/manage_proj_cat_delete.php @@ -30,6 +30,7 @@ auth_reauthenticate(); $f_category_id = gpc_get_string( 'id' ); + $f_project_id = gpc_get_int( 'project_id' ); access_ensure_project_level( config_get( 'manage_project_threshold' ), $f_project_id ); @@ -44,7 +45,11 @@ category_remove( $f_category_id ); - $t_redirect_url = 'manage_proj_edit_page.php?project_id=' . $t_project_id; + if ( $f_project_id == ALL_PROJECTS ) { + $t_redirect_url = 'manage_proj_page.php'; + } else { + $t_redirect_url = 'manage_proj_edit_page.php?project_id=' . $f_project_id; + } html_page_top1(); html_meta_redirect( $t_redirect_url ); diff --git a/manage_proj_cat_edit_page.php b/manage_proj_cat_edit_page.php index 781d938..2491518 100644 --- a/manage_proj_cat_edit_page.php +++ b/manage_proj_cat_edit_page.php @@ -30,6 +30,7 @@ auth_reauthenticate(); $f_category_id = gpc_get_string( 'id' ); + $f_project_id = gpc_get_string( 'project_id' ); access_ensure_project_level( config_get( 'manage_project_threshold' ), $f_project_id ); diff --git a/manage_proj_cat_update.php b/manage_proj_cat_update.php index 98610e8..7c736f4 100644 --- a/manage_proj_cat_update.php +++ b/manage_proj_cat_update.php @@ -30,6 +30,7 @@ auth_reauthenticate(); $f_category_id = gpc_get_int( 'category_id' ); + $f_project_id = gpc_get_int( 'project_id' ); $f_name = trim( gpc_get_string( 'name' ) ); $f_assigned_to = gpc_get_int( 'assigned_to', 0 ); @@ -50,7 +51,11 @@ category_update( $f_category_id, $f_name, $f_assigned_to ); - $t_redirect_url = 'manage_proj_edit_page.php?project_id=' . $t_project_id; + if ( $f_project_id == ALL_PROJECTS ) { + $t_redirect_url = 'manage_proj_page.php'; + } else { + $t_redirect_url = 'manage_proj_edit_page.php?project_id=' . $f_project_id; + } html_page_top1(); diff --git a/manage_proj_create.php b/manage_proj_create.php index 3dcd7a4..9d7d87b 100644 --- a/manage_proj_create.php +++ b/manage_proj_create.php @@ -36,8 +36,10 @@ $f_view_state = gpc_get_int( 'view_state' ); $f_status = gpc_get_int( 'status' ); $f_file_path = gpc_get_string( 'file_path', '' ); + $f_inherit_global = gpc_get_bool( 'inherit_global', 1 ); + $f_inherit_parent = gpc_get_bool( 'inherit_parent', 1 ); - $t_project_id = project_create( strip_tags( $f_name ), $f_description, $f_status, $f_view_state, $f_file_path ); + $t_project_id = project_create( strip_tags( $f_name ), $f_description, $f_status, $f_view_state, $f_file_path, true, $f_inherit_global ); if ( ( $f_view_state == VS_PRIVATE ) && ( false === current_user_is_administrator() ) ) { $t_access_level = access_get_global_level(); @@ -48,7 +50,7 @@ $f_parent_id = gpc_get_int( 'parent_id', 0 ); if ( 0 != $f_parent_id ) { - project_hierarchy_add( $t_project_id, $f_parent_id ); + project_hierarchy_add( $t_project_id, $f_parent_id, $f_inherit_parent ); } $t_redirect_url = 'manage_proj_page.php'; diff --git a/manage_proj_create_page.php b/manage_proj_create_page.php index bf6f7c2..6714c0c 100644 --- a/manage_proj_create_page.php +++ b/manage_proj_create_page.php @@ -84,6 +84,24 @@ + + + + + + + + + + + + + + + + + + diff --git a/manage_proj_edit_page.php b/manage_proj_edit_page.php index 5292cdd..c819219 100644 --- a/manage_proj_edit_page.php +++ b/manage_proj_edit_page.php @@ -95,6 +95,16 @@ + +> + + + + + /> + + + > @@ -179,7 +189,10 @@ if ( access_has_global_level ( config_get( 'delete_project_threshold' ) ) ) { ?> +
+ + + + - + @@ -208,18 +224,22 @@ if ( access_has_global_level ( config_get( 'delete_project_threshold' ) ) ) { ?> > - + - + - + + /> + + @@ -227,9 +247,8 @@ if ( access_has_global_level ( config_get( 'delete_project_threshold' ) ) ) { ?> @@ -238,9 +257,16 @@ if ( access_has_global_level ( config_get( 'delete_project_threshold' ) ) ) { ?> } # End of hiding subproject listing if there are no subprojects ?> + + + +
+ + + - +
+ + +
+ + + + + diff --git a/manage_proj_update.php b/manage_proj_update.php index 2ddbdd2..3cbd2e6 100644 --- a/manage_proj_update.php +++ b/manage_proj_update.php @@ -32,10 +32,11 @@ $f_view_state = gpc_get_int( 'view_state' ); $f_file_path = gpc_get_string( 'file_path', '' ); $f_enabled = gpc_get_bool( 'enabled' ); + $f_inherit_global = gpc_get_bool( 'inherit_global', 1 ); access_ensure_project_level( config_get( 'manage_project_threshold' ), $f_project_id ); - project_update( $f_project_id, $f_name, $f_description, $f_status, $f_view_state, $f_file_path, $f_enabled ); + project_update( $f_project_id, $f_name, $f_description, $f_status, $f_view_state, $f_file_path, $f_enabled, $f_inherit_global ); print_header_redirect( 'manage_proj_page.php' ); ?> diff --git a/manage_proj_update_children.php b/manage_proj_update_children.php new file mode 100644 index 0000000..3a7cbe6 --- /dev/null +++ b/manage_proj_update_children.php @@ -0,0 +1,39 @@ +. + + require_once( 'core.php' ); + + $t_core_path = config_get( 'core_path' ); + + require_once( $t_core_path.'project_hierarchy_api.php' ); + + auth_reauthenticate(); + + $f_project_id = gpc_get_int( 'project_id' ); + + access_ensure_project_level( config_get( 'manage_project_threshold' ), $f_project_id ); + + $t_subproject_ids = current_user_get_accessible_subprojects( $f_project_id, true ); + foreach ( $t_subproject_ids as $t_subproject_id ) { + $f_inherit_child = gpc_get_bool( 'inherit_child_' . $t_subproject_id, false ); + var_dump( $t_subproject_id, $f_project_id, $f_inherit_child ); + project_hierarchy_update( $t_subproject_id, $f_project_id, $f_inherit_child ); + } + + print_successful_redirect( 'manage_proj_edit_page.php?project_id=' . $f_project_id );