View Issue Details

IDProjectCategoryView StatusLast Update
0008130mantisbtrelationshipspublic2012-11-12 06:10
Reporterkratib Assigned Tograngeway  
PrioritynormalSeverityfeatureReproducibilityN/A
Status closedResolutionfixed 
Product Version1.0.8 
Fixed in Version1.1.0a4 
Summary0008130: Custom relationships
Description

I have implemented a simple generic relationship system for 1.0.8. The way it works mimics other Mantis customizations:

  1. Create a file called custom_relationships_inc.php, in the Mantis root. In this file you declare your custom relationships in the following manner:
    [code]
    define( BUG_CUSTOM_RELATIONSHIP_FORWARD, 99 );
    define( BUG_CUSTOM_RELATIONSHIP_BACKWARD, 98 );

$g_relationships[ BUG_CUSTOM_RELATIONSHIP_FORWARD ] = array(
'#forward' = TRUE,
'#complementary' = BUG_CUSTOM_RELATIONSHIP_BACKWARD,
'#description' = 'rel_forward',
'#notify_added' = 'email_notification_title_for_action_forward_relationship_added',
'#notify_deleted' = 'email_notification_title_for_action_forward_relationship_deleted',
'#edge_style' = array ('style' => 'dashed','color' => '#808080'),
[/code]

...and the same for BUG_CUSTOM_RELATIONSHIP_BACKWARD.

  1. Add the translation strings in custom_string_inc.php
    [code]
    $s_rel_forward = 'origin of';
    $s_rel_backward = 'originates from';
    $s_email_notification_title_for_action_forward_relationship_added = 'foo foo';
    $s_email_notification_title_for_action_forward_relationship_deleted = 'bar bar';
    [/code]

That's it! The new relationships should appear on the bug page, in email notifications, and on bug graphs.

So what do the $g_relationships settings mean?

  • The array index is the relationship type ID that you define for your custom relationships.
  • '#forward' is a boolean that specifies the conceptual direction of the relationship: is it forward from this bug to the one specified, or backward from the one specified to this one?
  • '#complementary' specifies the ID of the complementary relationship: for 'parent', it's 'child', etc. Some relationships are their own complementary; in this case just use the same type ID as the array index.
  • '#description' is the string name that Mantis tries to translate in custom_strings_inc.php
  • '#notify_added' and '#notify_deleted' are the string names for email messages when this relationship is modified in a bug.
  • '#edge_style' is the Graphviz edge style to be used for this relationship type.

For now all these fields are mandatory.

TagsNo tags attached.
Attached Files
custom_relationships.patch (14,150 bytes)   
diff -pur mantis-1.0.8/core/email_api.php /var/www/mantis/core/email_api.php
--- mantis-1.0.8/core/email_api.php	2007-03-06 07:54:14.000000000 +0200
+++ /var/www/mantis/core/email_api.php	2007-07-05 17:55:49.000000000 +0300
@@ -17,6 +17,7 @@
 	require_once( $t_core_dir . 'custom_field_api.php' );
 	require_once( $t_core_dir . 'string_api.php' );
 	require_once( $t_core_dir . 'history_api.php' );
+	require_once( $t_core_dir . 'relationship_api.php' );
 	require_once( PHPMAILER_PATH . 'class.phpmailer.php' );
 
 	# reusable object of class SMTP
@@ -463,26 +464,11 @@
 	function email_relationship_added( $p_bug_id, $p_related_bug_id, $p_rel_type ) {
 		$t_opt = array();
 		$t_opt[] = bug_format_id( $p_related_bug_id );
-		switch ( $p_rel_type ) {
-			case BUG_BLOCKS:
-				email_generic( $p_bug_id, 'relation', 'email_notification_title_for_action_blocks_relationship_added', $t_opt );
-				break;
-			case BUG_DEPENDANT:
-				email_generic( $p_bug_id, 'relation', 'email_notification_title_for_action_dependant_on_relationship_added', $t_opt );
-				break;
-			case BUG_HAS_DUPLICATE:
-				email_generic( $p_bug_id, 'relation', 'email_notification_title_for_action_has_duplicate_relationship_added', $t_opt );
-				break;
-			case BUG_DUPLICATE:
-				email_generic( $p_bug_id, 'relation', 'email_notification_title_for_action_duplicate_of_relationship_added', $t_opt );
-				break;
-			case BUG_RELATED:
-				email_generic( $p_bug_id, 'relation', 'email_notification_title_for_action_related_to_relationship_added', $t_opt );
-				break;
-			default:
-				trigger_error( ERROR_RELATIONSHIP_NOT_FOUND, ERROR );
-				break;
-			}
+		global $g_relationships;
+		if ( !isset( $g_relationships[ $p_rel_type ] ) ) {
+			trigger_error( ERROR_RELATIONSHIP_NOT_FOUND, ERROR );
+		}
+		email_generic( $p_bug_id, 'relation', $g_relationships[ $p_rel_type ][ '#notify_added' ], $t_opt );
 	}
 
 	# --------------------
@@ -491,26 +477,11 @@
 	function email_relationship_deleted( $p_bug_id, $p_related_bug_id, $p_rel_type ) {
 		$t_opt = array();
 		$t_opt[] = bug_format_id( $p_related_bug_id );
-		switch ( $p_rel_type ) {
-			case BUG_BLOCKS:
-				email_generic( $p_bug_id, 'relation', 'email_notification_title_for_action_blocks_relationship_deleted', $t_opt );
-				break;
-			case BUG_DEPENDANT:
-				email_generic( $p_bug_id, 'relation', 'email_notification_title_for_action_dependant_on_relationship_deleted', $t_opt );
-				break;
-			case BUG_HAS_DUPLICATE:
-				email_generic( $p_bug_id, 'relation', 'email_notification_title_for_action_has_duplicate_relationship_deleted', $t_opt );
-				break;
-			case BUG_DUPLICATE:
-				email_generic( $p_bug_id, 'relation', 'email_notification_title_for_action_duplicate_of_relationship_deleted', $t_opt );
-				break;
-			case BUG_RELATED:
-				email_generic( $p_bug_id, 'relation', 'email_notification_title_for_action_related_to_relationship_deleted', $t_opt );
-				break;
-			default:
-				trigger_error( ERROR_RELATIONSHIP_NOT_FOUND, ERROR );
-				break;
-			}
+		global $g_relationships;
+		if ( !isset( $g_relationships[ $p_rel_type ] ) ) {
+			trigger_error( ERROR_RELATIONSHIP_NOT_FOUND, ERROR );
+		}
+		email_generic( $p_bug_id, 'relation', $g_relationships[ $p_rel_type ][ '#notify_deleted' ], $t_opt );
 	}
 
 	# --------------------
diff -pur mantis-1.0.8/core/relationship_api.php /var/www/mantis/core/relationship_api.php
--- mantis-1.0.8/core/relationship_api.php	2005-06-28 14:04:06.000000000 +0300
+++ /var/www/mantis/core/relationship_api.php	2007-07-05 19:06:56.000000000 +0300
@@ -67,43 +67,71 @@
 		var $dest_project_id;
 		var $type;
 	}
+	
+	$g_relationships = array();
+	$g_relationships[ BUG_DEPENDANT ] = array(
+	'#forward' => TRUE,
+	'#complementary' => BUG_BLOCKS,
+	'#description' => 'dependant_on',
+	'#notify_added' => 'email_notification_title_for_action_dependant_on_relationship_added',
+	'#notify_deleted' => 'email_notification_title_for_action_dependant_on_relationship_deleted',
+	'#edge_style' => array ('color' => '#C00000','dir' => 'back'),
+	);
+	$g_relationships[ BUG_BLOCKS ] = array(
+	'#forward' => FALSE,
+	'#complementary' => BUG_DEPENDANT,
+	'#description' => 'blocks',
+	'#notify_added' => 'email_notification_title_for_action_blocks_relationship_added',
+	'#notify_deleted' => 'email_notification_title_for_action_blocks_relationship_deleted',
+	'#edge_style' => array ('color' => '#C00000','dir' => 'forward'),
+	);
+	$g_relationships[ BUG_DUPLICATE ] = array(
+	'#forward' => TRUE,
+	'#complementary' => BUG_HAS_DUPLICATE,
+	'#description' => 'duplicate_of',
+	'#notify_added' => 'email_notification_title_for_action_duplicate_of_relationship_added',
+	'#notify_deleted' => 'email_notification_title_for_action_duplicate_of_relationship_deleted',
+	'#edge_style' => array ('style' => 'dashed','color' => '#808080'),
+	);
+	$g_relationships[ BUG_HAS_DUPLICATE ] = array(
+	'#forward' => FALSE,
+	'#complementary' => BUG_DUPLICATE,
+	'#description' => 'has_duplicate',
+	'#notify_added' => 'email_notification_title_for_action_has_duplicate_relationship_added',
+	'#notify_deleted' => 'email_notification_title_for_action_has_duplicate_relationship_deleted',
+	);
+	$g_relationships[ BUG_RELATED ] = array(
+	'#forward' => TRUE,
+	'#complementary' => BUG_RELATED,
+	'#description' => 'related_to',
+	'#notify_added' => 'email_notification_title_for_action_related_to_relationship_added',
+	'#notify_deleted' => 'email_notification_title_for_action_related_to_relationship_deleted',
+	);
+	if ( file_exists( dirname( dirname( __FILE__ ) ).DIRECTORY_SEPARATOR.'custom_relationships_inc.php' ) ) {
+		require_once( dirname( dirname( __FILE__ ) ).DIRECTORY_SEPARATOR.'custom_relationships_inc.php' );
+	}    
+
 
 	# --------------------
 	# Return the complementary type of the provided relationship
 	function relationship_get_complementary_type( $p_relationship_type ) {
-		switch ( $p_relationship_type ) {
-			case BUG_BLOCKS:
-				return BUG_DEPENDANT;
-				break;
-			case BUG_DEPENDANT:
-				return BUG_BLOCKS;
-				break;
-			case BUG_HAS_DUPLICATE:
-				return BUG_DUPLICATE;
-				break;
-			case BUG_DUPLICATE:
-				return BUG_HAS_DUPLICATE;
-				break;
-			case BUG_RELATED:
-				return BUG_RELATED;
-				break;
-			default:
-				trigger_error( ERROR_GENERIC, ERROR );
-				break;
+	        global $g_relationships;
+		if ( !isset( $g_relationships[ $p_relationship_type ] ) ) {
+			trigger_error( ERROR_GENERIC, ERROR );
 		}
+		return $g_relationships[ $p_relationship_type ][ '#complementary' ];
 	}
 
 	# --------------------
 	function relationship_add( $p_src_bug_id, $p_dest_bug_id, $p_relationship_type ) {
 		$t_mantis_bug_relationship_table = config_get( 'mantis_bug_relationship_table' );
 
-		if( $p_relationship_type == BUG_BLOCKS || $p_relationship_type == BUG_HAS_DUPLICATE ) {
-			# BUG_BLOCKS or BUG_HAS_DUPLICATE -> swap src and dest
+		global $g_relationships;
+		if ( $g_relationships[ $p_relationship_type ][ '#forward' ] === FALSE ) {
 			$c_src_bug_id = db_prepare_int( $p_dest_bug_id );
 			$c_dest_bug_id = db_prepare_int( $p_src_bug_id );
 			$c_relationship_type = db_prepare_int( relationship_get_complementary_type( $p_relationship_type ) );
-		}
-		else {
+		} else {
 			$c_src_bug_id = db_prepare_int( $p_src_bug_id );
 			$c_dest_bug_id = db_prepare_int( $p_dest_bug_id );
 			$c_relationship_type = db_prepare_int( $p_relationship_type );
@@ -129,13 +157,12 @@
 	function relationship_update( $p_relationship_id, $p_src_bug_id, $p_dest_bug_id, $p_relationship_type ) {
 		$t_mantis_bug_relationship_table = config_get( 'mantis_bug_relationship_table' );
 
-		if( $p_relationship_type == BUG_BLOCKS || $p_relationship_type == BUG_HAS_DUPLICATE ) {
-			# BUG_BLOCKS or BUG_HAS_DUPLICATE -> swap src and dest
+		global $g_relationships;
+		if ( $g_relationships[ $p_relationship_type ][ '#forward' ] === FALSE ) {
 			$c_src_bug_id = db_prepare_int( $p_dest_bug_id );
 			$c_dest_bug_id = db_prepare_int( $p_src_bug_id );
 			$c_relationship_type = db_prepare_int( relationship_get_complementary_type( $p_relationship_type ) );
-		}
-		else {
+		} else {
 			$c_src_bug_id = db_prepare_int( $p_src_bug_id );
 			$c_dest_bug_id = db_prepare_int( $p_dest_bug_id );
 			$c_relationship_type = db_prepare_int( $p_relationship_type );
@@ -412,61 +439,28 @@
 	# --------------------
 	# get class description of a relationship (source side)
 	function relationship_get_description_src_side( $p_relationship_type ) {
-		switch ( $p_relationship_type ) {
-			case BUG_DUPLICATE:
-				return lang_get( 'duplicate_of' );
-				break;
-			case BUG_RELATED:
-				return lang_get( 'related_to' ) ;
-				break;
-			case BUG_DEPENDANT:
-				return lang_get( 'dependant_on' ) ;
-				break;
-			default:
-				trigger_error( ERROR_RELATIONSHIP_NOT_FOUND, ERROR );
+		global $g_relationships;
+		if ( !isset( $g_relationships[ $p_relationship_type ] ) ) {
+			trigger_error( ERROR_RELATIONSHIP_NOT_FOUND, ERROR );
 		}
+		return lang_get( $g_relationships[ $p_relationship_type ][ '#description' ] ); 
 	}
 
 	# --------------------
 	# get class description of a relationship (destination side)
 	function relationship_get_description_dest_side( $p_relationship_type ) {
-		switch ( $p_relationship_type ) {
-			case BUG_DUPLICATE:
-				return lang_get( 'has_duplicate' ) ;
-				break;
-			case BUG_RELATED:
-				return lang_get( 'related_to' ) ;
-				break;
-			case BUG_DEPENDANT:
-				return lang_get( 'blocks' ) ;
-				break;
-			default:
-				trigger_error( ERROR_RELATIONSHIP_NOT_FOUND, ERROR );
+		global $g_relationships;
+		if ( !isset( $g_relationships[ $p_relationship_type ] ) ||
+		     !isset( $g_relationships[ $g_relationships[ $p_relationship_type ][ '#complementary' ] ] ) ) {
+			trigger_error( ERROR_RELATIONSHIP_NOT_FOUND, ERROR );
 		}
+		return lang_get( $g_relationships[ $g_relationships[ $p_relationship_type ][ '#complementary' ] ][ '#description' ] ); 
 	}
 
 	# --------------------
 	# get class description of a relationship as it's stored in the history
 	function relationship_get_description_for_history( $p_relationship_code ) {
-		switch ( $p_relationship_code ) {
-			case BUG_HAS_DUPLICATE:
-				return lang_get( 'has_duplicate' ) ;
-				break;
-			case BUG_DUPLICATE:
-				return lang_get( 'duplicate_of' );
-				break;
-			case BUG_BLOCKS:
-				return lang_get( 'blocks' ) ;
-				break;
-			case BUG_DEPENDANT:
-				return lang_get( 'dependant_on' ) ;
-				break;
-			case BUG_RELATED:
-				return lang_get( 'related_to' ) ;
-				break;
-			default:
-				trigger_error( ERROR_RELATIONSHIP_NOT_FOUND, ERROR );
-		}
+		return relationship_get_description_src_side( $p_relationship_code );
 	}
 
 	# --------------------
@@ -683,6 +677,7 @@
  	# --------------------
  	# print HTML relationship listbox
 	function relationship_list_box( $p_default_rel_type = -1, $p_select_name = "rel_type", $p_include_any = false) {
+		global $g_relationships;
 ?>
 <select name="<?php echo $p_select_name?>">
 <?php if ($p_include_any) { ?>
@@ -690,24 +685,11 @@
 <?php
     }
 ?>
-<option value="<?php echo BUG_RELATED ?>"<?php echo ( $p_default_rel_type == BUG_RELATED ? ' selected' : '' ) ?>><?php echo lang_get( 'related_to' ) ?></option>
-<option value="<?php echo BUG_DEPENDANT ?>"<?php echo ( $p_default_rel_type == BUG_DEPENDANT ? ' selected' : '' ) ?>><?php echo lang_get( 'dependant_on' ) ?></option>
-<option value="<?php echo BUG_BLOCKS ?>" <?php echo ( $p_default_rel_type == BUG_BLOCKS ? ' selected' : '' ) ?>><?php echo lang_get( 'blocks' ) ?></option>
-<option value="<?php echo BUG_DUPLICATE ?>"<?php echo ( $p_default_rel_type == BUG_DUPLICATE ? ' selected' : '' ) ?>><?php echo lang_get( 'duplicate_of' ) ?></option>
-<option value="<?php echo BUG_HAS_DUPLICATE ?>"<?php echo ( $p_default_rel_type == BUG_HAS_DUPLICATE ? ' selected' : '' ) ?>><?php echo lang_get( 'has_duplicate' ) ?></option>
-</select>
+<?php foreach ( $g_relationships as $type => $relationship ) { ?>
+<option value="<?php echo $type ?>"<?php echo ( $p_default_rel_type == $type ? ' selected' : '' ) ?>><?php echo lang_get( $relationship['#description'] ) ?></option>
 <?php
-	}
-
- 	# --------------------
- 	# print HTML relationship listbox
-	function relationship_list_box_for_cloned_bug( $p_default_rel_type = -1 ) {
+    }
 ?>
-<select name="rel_type">
-<option value="-1"<?php echo ( $p_default_rel_type == -1 ? ' selected' : '' ) ?>><?php echo lang_get( 'no_relationship' ) ?></option>
-<option value="<?php echo BUG_RELATED ?>"<?php echo ( $p_default_rel_type == BUG_RELATED ? ' selected' : '' ) ?>><?php echo lang_get( 'related_to' ) ?></option>
-<option value="<?php echo BUG_DEPENDANT ?>"<?php echo ( $p_default_rel_type == BUG_DEPENDANT ? ' selected' : '' ) ?>><?php echo lang_get( 'dependant_on' ) ?></option>
-<option value="<?php echo BUG_BLOCKS ?>" <?php echo ( $p_default_rel_type == BUG_BLOCKS ? ' selected' : '' ) ?>><?php echo lang_get( 'blocks' ) ?></option>
 </select>
 <?php
 	}
diff -pur mantis-1.0.8/core/relationship_graph_api.php /var/www/mantis/core/relationship_graph_api.php
--- mantis-1.0.8/core/relationship_graph_api.php	2005-04-22 23:34:04.000000000 +0200
+++ /var/www/mantis/core/relationship_graph_api.php	2007-07-05 19:05:25.000000000 +0300
@@ -157,32 +157,12 @@
 
 					$t_related_id	= bug_format_id( $t_dst );
 
-					switch ( $t_relation ) {
-						case BUG_DUPLICATE:
-							$t_edge_style = array (
-								'style' => 'dashed',
-								'color' => '#808080'
-							);
-							break;
-
-						case BUG_DEPENDANT:
-							$t_edge_style = array (
-								'color' => '#C00000',
-								'dir' => 'back'
-							);
-							break;
-
-						case BUG_BLOCKS:
-							$t_edge_style = array (
-								'color' => '#C00000',
-								'dir' => 'forward'
-							);
-							break;
-
-						default:
-							$t_edge_style = array ( );
-							break;
-					}
+          global $g_relationships;
+          if (isset($g_relationships[ $t_relation ])) {
+            $t_edge_style = $g_relationships[ $t_relation ][ '#edge_style' ];
+          } else {
+            $t_edge_style = array ( );
+          }
 
 					$t_graph->add_edge( $t_id_string, $t_related_id, $t_edge_style );
 				}
custom_relationships.patch (14,150 bytes)   

Relationships

has duplicate 0007571 closedvboctor New relationship "superseds" (and "superseded by") 
has duplicate 0004299 closedvboctor New relationship types 
related to 0013203 acknowledged incompletely customized relationships 
related to 0004639 acknowledged New relationship "predecessor / successor" 

Activities

grangeway

grangeway

2007-07-27 19:23

reporter   ~0015248

I've commited this code to CVS (albeit with one change in relationship graph:
if ( isset( $g_relationships[ $t_relation ] ) && isset( $g_relationships[ $t_relation ][ '#edge_style' ] ) ) {
$t_edge_style = $g_relationships[ $t_relation ][ '#edge_style' ];

I believe this will fix a warning.

The new code you submit IMO, does tidy up the API a little. I was very tempted to remove the 3 lines of code to include custom_relationships_inc.php as it does seem like a confusing/pointless feature. I'm struggling to think of real life cases where the a bug would not be related to another bug, nor a child / parent or duplicate of another issue.

For now the custom include field is included in the commit, however if another developer thinks that this is likely to cause support issues with users, i'd be inclined to remove the support for the reasons I state in my view above.

Paul

vboctor

vboctor

2007-07-29 02:16

manager   ~0015268

I think it is neat to allow for such customization, specially if we can make it without added complexity. So given that grangeway likes the new API better than the old one, I think we should stick with it for now.

I've put together a HOW TO page for this page. The instructions about has several syntax errors and issues with it. However, it was a good start for me to come up with a more detailed instructions that worked fine on my installation.

http://www.mantisbt.org/wiki/doku.php/mantisbt:customizing_relationships

Related Changesets

MantisBT: master fead6728

2007-07-27 19:23

Paul Richards


Details Diff
0008130: Custom relationships

Include this patch mainly as I believe it does tidy up the existing API for future use.
If someone feels that the customisation part of this could cause support issues, i'd be inclined to remove the 3 lines relating to this part, as per my comment in bug 8130

git-svn-id: http://mantisbt.svn.sourceforge.net/svnroot/mantisbt/trunk@4498 <a class="text" href="/?p=mantisbt.git;a=object;h=f5dc347c">f5dc347c</a>-c33d-0410-90a0-b07cc1902cb9
Affected Issues
0008130
mod - core/email_api.php Diff File
mod - core/relationship_graph_api.php Diff File
mod - core/relationship_api.php Diff File