View Issue Details

IDProjectCategoryView StatusLast Update
0009025mantisbtjavascriptpublic2008-04-19 04:10
Reporterjreese Assigned Tojreese  
PrioritynormalSeverityfeatureReproducibilityN/A
Status closedResolutionfixed 
Target Version1.2.0a1Fixed in Version1.2.0a1 
Summary0009025: Enhance Collapse API for Extensiblity and Plugin Usage
Description

In order for the collapse API to become usable for more than a select
few items in Mantis, it needs to be extensible and useful without the
need to preset a bunch of javascript values. This will be very
important for use in plugins.

What I have decided would be the best course of action is to modify the collapse API to use the tokens system to track user collapse preferences in the database according to collapse block names, rather than a javascript/cookie bitmask. At render time, the collapse blocks are pre-assigned opened/closed status based on the user's tokens. When the user toggles a collapse block, a cookie is set on the user's browser; at the next page load, the cookie is retrieved, and the user's token is updated accordingly. This also means that a user can move to any browser, and their collapse preferences will be maintained.

The new system is now able to easily expand to any future usage without requiring changes to the javascript library, and without assigning arbitrary bitmasks to every collapse block.

TagsNo tags attached.
Attached Files
mantis-collapse.2008-04-01.patch (10,415 bytes)   
diff --git a/core.php b/core.php
index b2a248e..23a7297 100644
--- a/core.php
+++ b/core.php
@@ -149,6 +149,8 @@
 	require_once( $t_core_path.'html_api.php' );
 	require_once( $t_core_path.'gpc_api.php' );
 	require_once( $t_core_path.'print_api.php' );
+	require_once( $t_core_path.'collapse_api.php' );
+	collapse_cache_token();
 
 	# custom functions (in main directory)
 	# @@@ Move all such files to core/
diff --git a/core/collapse_api.php b/core/collapse_api.php
index c32ec8a..49d7ec4 100644
--- a/core/collapse_api.php
+++ b/core/collapse_api.php
@@ -22,6 +22,7 @@
 	# --------------------------------------------------------
 
 	$t_core_dir = dirname( __FILE__ ).DIRECTORY_SEPARATOR;
+	require_once( $t_core_dir . 'tokens_api.php' );
 
 	### Collapse API ###
 
@@ -36,39 +37,52 @@
 	#	:
 	#	collapse_end( 'xyz' );		# marks the end of the whole section
 	#
-	# In javascript/common.js, add the g_div_xyz constants.
-	#
 
 	$g_current_collapse_section = null;
 	$g_open_collapse_section = false;
 
-	# ---------------
-	# Use at the top of the section that should be visible when the section is expanded.
-	# sections can not be nested
-	function collapse_open( $p_name ) {
+	$g_collapse_cache_token = null;
+
+	/**
+	 * Marks the beginning of a collapse block's open phase.
+	 * This will be visible if the block is expanded, or if
+	 * javascript is disabled.
+	 * @param string Collapse block name
+	 * @param string Collapse block section
+	 */
+	function collapse_open( $p_name, $p_section = '' ) {
 		global $g_current_collapse_section, $g_open_collapse_section;
 
+		$t_block = ( is_blank( $p_section ) ? $p_name : $p_section . '_' . $p_name );
+		$t_display = collapse_display( $t_block );
+
 		# make sure no other collapse section is started
 		if ( $g_current_collapse_section !== null ) {
 			trigger_error( ERROR_GENERIC, ERROR );
 		}
 
 		$g_open_collapse_section = true;
-		$g_current_collapse_section = $p_name;
+		$g_current_collapse_section = $t_block;
 
-		$t_div_id = $p_name . '_open';
-		echo "<div id=\"$t_div_id\">";
+		$t_div_id = $t_block . '_open';
+		echo '<div id="', $t_div_id, '"', ( $t_display ? '' : ' style="display: none"' ) ,'>';
 	}
 
-	# ---------------
-	# Use to mark the end of the expanded section and the beginning of the closed section
-	# the closed section will not be sent to the browser if $g_Use_javascript is OFF.
-	# This is achieved using output buffering.
-	function collapse_closed( $p_name ) {
+	/**
+	 * Marks the end of a collapse block's open phase and the beginning
+	 * of the block's closed phase.  Thi will only be visible if the
+	 * block have been collapsed and javascript is enabled.
+	 * @param string Collapse block name
+	 * @param string Collapse block section
+	 */
+	function collapse_closed( $p_name, $p_section = '' ) {
 		global $g_current_collapse_section, $g_open_collapse_section;
 
+		$t_block = ( is_blank( $p_section ) ? $p_name : $p_section . '_' . $p_name );
+		$t_display = !collapse_display( $t_block );
+
 		# Make sure a section is opened, and it is the same section.
-		if ( $p_name !== $g_current_collapse_section ) {
+		if ( $t_block !== $g_current_collapse_section ) {
 			trigger_error( ERROR_GENERIC, ERROR );
 		}
 
@@ -78,17 +92,24 @@
 
 		ob_start();
 
-		echo '<div id="', $p_name, '_closed" style="display: none;">';
+		$t_div_id = $t_block . '_closed';
+		echo '<div id="', $t_div_id, '"', ( $t_display ? '' : ' style="display: none"' ) ,'>';
 	}
 
-	# ---------------
-	# This is used within both the open and closed section to identify the location where the
-	# '+'/'-' icon should be placed.
-	function collapse_icon( $p_name ) {
+	/**
+	 * Marks the location where a +/- icon is placed in output
+	 * for the user to toggle the collapse block status.
+	 * This should appear in both the open and closed phase of a block.
+	 * @param string Collapse block name
+	 * @param string Collapse block section
+	 */
+	function collapse_icon( $p_name, $p_section = '' ) {
 		if ( OFF == config_get( 'use_javascript' ) ) {
 			return;
 		}
 
+		$t_block = ( is_blank( $p_section ) ? $p_name : $p_section . '_' . $p_name );
+
 		global $g_open_collapse_section;
 
 		if ( $g_open_collapse_section === true ) {
@@ -99,18 +120,24 @@
 			$t_alt  = '+';
 		}
 
-		echo "<a href=\"\" onclick=\"ToggleDiv( '$p_name', g_div_$p_name ); return false;\"
+		echo "<a href=\"\" onclick=\"ToggleDiv( '$t_block' ); return false;\"
 			><img border=\"0\" src=\"images/$t_icon\" alt=\"$t_alt\" /></a>&nbsp;";
 	}
 
-	# ---------------
-	# Mark the end of the collapsible section
-	function collapse_end( $p_name ) {
+	/**
+	 * Marks the end of a collaps block's closed phase.
+	 * Closed phase output is discarded if javascript is disabled.
+	 * @param string Collapse block name
+	 * @param string Collapse block section
+	 */
+	function collapse_end( $p_name, $p_section = '' ) {
 		global $g_current_collapse_section, $g_open_collapse_section;
 
+		$t_block = ( is_blank( $p_section ) ? $p_name : $p_section . '_' . $p_name );
+		$t_display = collapse_display( $t_block );
 
 		# Make sure a section is opened, and it is the same section.
-		if ( $p_name !== $g_current_collapse_section ) {
+		if ( $t_block !== $g_current_collapse_section ) {
 			ob_end_clean();
 			trigger_error( ERROR_GENERIC, ERROR );
 		}
@@ -120,10 +147,6 @@
 		$g_open_collapse_section = false;
 
 		if ( ON == config_get( 'use_javascript' ) ) {
-			echo '<script type="text/javascript" language="JavaScript"><!--' . "\n";
-			echo '	SetDiv( "', $p_name, '", g_div_', $p_name, ' );' . "\n";
-			echo '--></script>';
-
 			ob_end_flush();
 		} else {
 			ob_end_clean();
@@ -131,4 +154,72 @@
 
 		$g_current_collapse_section = null;
 	}
+
+	/**
+	 * Determine if a block should be displayed open by default.
+	 * @param string Collapse block
+	 */
+	function collapse_display( $p_block ) {
+		global $g_collapse_cache_token;
+
+		if ( !isset( $g_collapse_cache_token[$p_block] ) || OFF == config_get( 'use_javascript' ) ) {
+			return true;
+		}
+
+		return ( true == $g_collapse_cache_token[$p_block] );
+	}
+
+	/**
+	 * Cache collapse API data from the database for the current user.
+	 * If the collapse cookie has been set, grab the changes and resave
+	 * the token, or touch it otherwise.
+	 */
+	function collapse_cache_token() {
+		global $g_collapse_cache_token;
+
+		if ( current_user_is_anonymous() ) {
+			$g_collapse_cache_token = array();
+			return;
+		}
+
+		if ( isset( $g_collapse_cache_token ) ) {
+			return;
+		}
+
+		$t_user_id = auth_get_current_user_id();
+		$t_token = token_get_value( TOKEN_COLLAPSE );
+
+		if ( !is_null( $t_token ) ) {
+			$t_data = unserialize( $t_token );
+		} else {
+			$t_data = array();
+		}
+
+		$g_collapse_cache_token = $t_data;
+
+		$t_cookie = gpc_get_cookie( 'MANTIS_collapse_settings', '' );
+
+		if ( false !== $t_cookie && !is_blank( $t_cookie ) ) {
+			$t_update = false;
+			$t_data = explode( '|', $t_cookie );
+
+			foreach ( $t_data as $t_pair ) {
+				$t_pair = explode( ',', $t_pair );
+
+				if ( false !== $t_pair && count( $t_pair ) == 2 ) {
+					$g_collapse_cache_token[$t_pair[0]] = ( true == $t_pair[1] );
+					$t_update = true;
+				}
+			}
+
+			if ( $t_update ) {
+				$t_token = serialize( $g_collapse_cache_token );
+				token_set( TOKEN_COLLAPSE, $t_token, TOKEN_EXPIRY_COLLAPSE );
+			} else {
+				token_touch( TOKEN_COLLAPSE );
+			}
+		}
+
+	}
+
 ?>
diff --git a/core/constant_inc.php b/core/constant_inc.php
index 412bc30..3ea6263 100644
--- a/core/constant_inc.php
+++ b/core/constant_inc.php
@@ -389,12 +389,14 @@
 	define( 'TOKEN_GRAPH',			2 );
 	define( 'TOKEN_LAST_VISITED',	3 );
 	define( 'TOKEN_AUTHENTICATED',	4 );
+	define( 'TOKEN_COLLAPSE',		5 );
 	define( 'TOKEN_USER',			1000 );
 
 	# token expirations
 	define( 'TOKEN_EXPIRY', 		60*60 ); # Default expiration of 60 minutes ( 3600 seconds )
 	define( 'TOKEN_EXPIRY_LAST_VISITED', 24*60*60 );
 	define( 'TOKEN_EXPIRY_AUTHENTICATED', 5*60 );
+	define( 'TOKEN_EXPIRY_COLLAPSE', 365*24*60*60 );
 
 	# config types
 	define( 'CONFIG_TYPE_INT', 1 );
diff --git a/javascript/common.js b/javascript/common.js
index 2375f97..10030bb 100644
--- a/javascript/common.js
+++ b/javascript/common.js
@@ -94,61 +94,29 @@ function SetCookie( p_cookie, p_value ) {
  * Collapsible element functions
  */
 
-var g_div_history       = 0x0001;
-var g_div_bugnotes      = 0x0002;
-var g_div_bugnote_add   = 0x0004;
-var g_div_bugnotestats  = 0x0008;
-var g_div_upload_form   = 0x0010;
-var g_div_monitoring    = 0x0020;
-var g_div_sponsorship   = 0x0040;
-var g_div_relationships = 0x0080;
-var g_div_filter        = 0x0100;
-
-var g_div_plugin		= 0x8000;
-
-/* List here the sections open by default */
-var g_default_view_settings = 
-	g_div_history | 
-	g_div_bugnotes |
-	g_div_bugnote_add |
-	g_div_bugnotestats |
-	g_div_upload_form |
-	g_div_monitoring |
-	g_div_sponsorship |
-	g_div_relationships;
-
-
-function GetViewSettings() {
-	var t_cookie = GetCookie( "VIEW_SETTINGS" );
-
-	if ( -1 == t_cookie ) {
-		t_cookie = g_default_view_settings;
-	} else {
-		t_cookie = parseInt( t_cookie );
-	}
+var g_collapse_clear = 1;
 
-	return t_cookie;
-}
+function ToggleDiv( p_div ) {
+	t_open_div = document.getElementById( p_div + "_open" )
+	t_closed_div = document.getElementById( p_div + "_closed" )
 
-function SetDiv( p_div, p_cookie_bit ) {
-	var t_view_settings = GetViewSettings();
+	t_cookie = GetCookie( "collapse_settings" );
+	if ( 1 == g_collapse_new ) {
+		t_cookie = "";
+		g_collapse_clear = 0;
+	}
 
-	if( t_view_settings & p_cookie_bit ) {
-		document.getElementById( p_div + "_open" ).style.display = "";
-		document.getElementById( p_div + "_closed" ).style.display = "none";
+	if ( t_open_div.style.display == "none" ) {
+		t_open_div.style.display = "";
+		t_closed_div.style.display = "none";
+		t_cookie = t_cookie + "|" + p_div + ",1"
 	} else {
-		document.getElementById( p_div + "_open" ).style.display = "none";
-		document.getElementById( p_div + "_closed" ).style.display = "";
+		t_closed_div.style.display = "";
+		t_open_div.style.display = "none";
+		t_cookie = t_cookie + "|" + p_div + ",0"
 	}
-}
-
-function ToggleDiv( p_div, p_cookie_bit ) {
-	var t_view_settings = GetViewSettings();
-
-	t_view_settings ^= p_cookie_bit;
-	SetCookie( "VIEW_SETTINGS", t_view_settings );
 
-	SetDiv( p_div, p_cookie_bit );
+	SetCookie( "collapse_settings", t_cookie );
 }
 
 /* Check checkboxes */

Relationships

related to 0009009 closedjreese Collapsed filter is shown expanded for half a second and the collapsed again 

Activities

jreese

jreese

2008-04-01 16:18

reporter   ~0017529

Changes committed to SVN trunk 1.2.x, r5151.