Skip to content

Commit

Permalink
Merge branch 'manage-config' into master-1.2.x
Browse files Browse the repository at this point in the history
This branch implements several improvements to the Manage Configuration
page, including:

 - better performance
 - filtering
 - ability to edit config options
  • Loading branch information
dregad committed Jan 22, 2013
2 parents 0052435 + d000c4d commit e539dd6
Show file tree
Hide file tree
Showing 11 changed files with 598 additions and 143 deletions.
348 changes: 307 additions & 41 deletions adm_config_report.php

Large diffs are not rendered by default.

212 changes: 162 additions & 50 deletions adm_config_set.php
Expand Up @@ -29,11 +29,11 @@

form_security_validate( 'adm_config_set' );

$f_user_id = gpc_get_int( 'user_id' );
$f_project_id = gpc_get_int( 'project_id' );
$f_user_id = gpc_get_int( 'user_id' );
$f_project_id = gpc_get_int( 'project_id' );
$f_config_option = gpc_get_string( 'config_option' );
$f_type = gpc_get_string( 'type' );
$f_value = gpc_get_string( 'value' );
$f_type = gpc_get_string( 'type' );
$f_value = gpc_get_string( 'value' );

if ( is_blank( $f_config_option ) ) {
error_parameters( 'config_option' );
Expand All @@ -60,52 +60,38 @@
trigger_error( ERROR_CONFIG_OPT_CANT_BE_SET_IN_DB, ERROR );
}

if ( $f_type === 'default' ) {
# For 'default', behavior is based on the global variable's type
if( $f_type == CONFIG_TYPE_DEFAULT ) {
$t_config_global_value = config_get_global( $f_config_option );
if ( is_string( $t_config_global_value ) ) {
$t_type = 'string';
} else if ( is_int( $t_config_global_value ) ) {
$t_type = 'integer';
} else { # note that we consider bool and float as complex. We use ON/OFF for bools which map to numeric.
$t_type = 'complex';
if( is_string( $t_config_global_value ) ) {
$t_type = CONFIG_TYPE_STRING;
} else if( is_int( $t_config_global_value ) ) {
$t_type = CONFIG_TYPE_INT;
} else if( is_float( $t_config_global_value ) ) {
$t_type = CONFIG_TYPE_FLOAT;
} else {
# note that we consider bool and float as complex.
# We use ON/OFF for bools which map to numeric.
$t_type = CONFIG_TYPE_COMPLEX;
}
} else {
$t_type = $f_type;
}

if ( $t_type === 'string' ) {
$t_value = $f_value;
} else if ( $t_type === 'integer' ) {
$t_value = (integer)$f_value;
} else {
# We support these kind of variables here:
# 1. constant values (like the ON/OFF switches): they are defined as constants mapping to numeric values
# 2. simple arrays with the form: array( a, b, c, d )
# 3. associative arrays with the form: array( a=>1, b=>2, c=>3, d=>4 )
# TODO: allow multi-dimensional arrays, allow commas and => within strings
$t_full_string = trim( $f_value );
if ( preg_match('/array[\s]*\((.*)\)/s', $t_full_string, $t_match ) === 1 ) {
// we have an array here
$t_values = explode( ',', trim( $t_match[1] ) );
foreach ( $t_values as $key => $value ) {
if ( !trim( $value ) ) {
continue;
}
$t_split = explode( '=>', $value, 2 );
if ( count( $t_split ) == 2 ) {
// associative array
$t_new_key = constant_replace( trim( $t_split[0], " \t\n\r\0\x0B\"'" ) );
$t_new_value = constant_replace( trim( $t_split[1], " \t\n\r\0\x0B\"'" ) );
$t_value[ $t_new_key ] = $t_new_value;
} else {
// regular array
$t_value[ $key ] = constant_replace( trim( $value, " \t\n\r\0\x0B\"'" ) );
}
}
} else {
// scalar value
$t_value = constant_replace( trim( $t_full_string ) );
}
switch( $t_type ) {
case CONFIG_TYPE_STRING:
$t_value = $f_value;
break;
case CONFIG_TYPE_INT:
$t_value = (integer) constant_replace( trim( $f_value ) );
break;
case CONFIG_TYPE_FLOAT:
$t_value = (float) constant_replace( trim( $f_value ) );
break;
case CONFIG_TYPE_COMPLEX:
default:
$t_value = process_complex_value( $f_value );
break;
}

config_set( $f_config_option, $t_value, $f_user_id, $f_project_id );
Expand All @@ -116,13 +102,139 @@


/**
* Check if the passed string is a constant and return its value
* Helper function to recursively process complex types
* We support the following kind of variables here:
* 1. constant values (like the ON/OFF switches): they are defined as constants mapping to numeric values
* 2. simple arrays with the form: array( a, b, c, d )
* 3. associative arrays with the form: array( a=>1, b=>2, c=>3, d=>4 )
* 4. multi-dimensional arrays
* commas and '=>' within strings are handled
*
* @param string $p_value Complex value to process
* @return parsed variable
*/
function process_complex_value( $p_value, $p_trimquotes = false ) {
static $s_regex_array = null;
static $s_regex_string = null;
static $s_regex_element = null;

$t_value = trim( $p_value );

# Parsing regex initialization
if( is_null( $s_regex_array ) ) {
$s_regex_array = '^array[\s]*\((.*)\)$';
$s_regex_string =
# unquoted string (word)
'[\w]+' . '|' .
# single-quoted string
"'(?:[^'\\\\]|\\\\.)*'" . '|' .
# double-quoted string
'"(?:[^"\\\\]|\\\\.)*"';
# The following complex regex will parse individual array elements,
# taking into consideration sub-arrays, associative arrays and single,
# double and un-quoted strings
# @TODO dregad reverse pattern logic for sub-array to avoid match on array(xxx)=>array(xxx)
$s_regex_element = '('
# Main sub-pattern - match one of
. '(' .
# sub-array: ungreedy, no-case match ignoring nested parenthesis
'(?:(?iU:array\s*(?:\\((?:(?>[^()]+)|(?1))*\\))))' . '|' .
$s_regex_string
. ')'
# Optional pattern for associative array, back-referencing the
# above main pattern
. '(?:\s*=>\s*(?2))?' .
')';
}

if( preg_match( "/$s_regex_array/s", $t_value, $t_match ) === 1 ) {
# It's an array - process each element
$t_processed = array();

if( preg_match_all( "/$s_regex_element/", $t_match[1], $t_elements ) ) {
foreach( $t_elements[0] as $key => $element ) {
if( !trim( $element ) ) {
# Empty element - skip it
continue;
}
# Check if element is associative array
preg_match_all( "/($s_regex_string)\s*=>\s*(.*)/", $element, $t_split );
if( !empty( $t_split[0] ) ) {
# associative array
$t_new_key = constant_replace( trim( $t_split[1][0], " \t\n\r\0\x0B\"'" ) );
$t_new_value = process_complex_value( $t_split[2][0], true );
$t_processed[$t_new_key] = $t_new_value;
} else {
# regular array
$t_new_value = process_complex_value( $element );
$t_processed[$key] = $t_new_value;
}
}
}
return $t_processed;
} else {
# Scalar value
if( $p_trimquotes ) {
$t_value = trim( $t_value, " \t\n\r\0\x0B\"'" );
}
return constant_replace( $t_value );
}
}

/**
* Split by commas, but ignore commas that are within quotes or parenthesis.
* Ignoring commas within parenthesis helps allow for multi-dimensional arrays.
* @param $p_string string to split
* @return array
*/
function special_split ( $p_string ) {
$t_values = array();
$t_array_element = "";
$t_paren_level = 0;
$t_inside_quote = False;
$t_escape_next = False;

foreach( str_split( trim( $p_string ) ) as $character ) {
if( $t_escape_next ) {
$t_array_element .= $character;
$t_escape_next = False;
} else if( $character == "," && $t_paren_level==0 && !$t_inside_quote ) {
array_push( $t_values, $t_array_element );
$t_array_element = "";
} else {
if( $character == "(" && !$t_inside_quote ) {
$t_paren_level ++;
} else if( $character == ")" && !$t_inside_quote ) {
$t_paren_level --;
} else if( $character == "'" ) {
$t_inside_quote = !$t_inside_quote;
} else if( $character == "\\" ) {
# escape character
$t_escape_next = true;
# keep the escape if the string will be going through another recursion
if( $t_paren_level > 0 ) {
$t_array_element .= $character;
}
continue;
}
$t_array_element .= $character;
}
}
array_push( $t_values, $t_array_element );
return $t_values;
}


/**
* Check if the passed string is a constant and returns its value
* if yes, or the string itself if not
* @param $p_name string to check
* @return mixed|string value of constant $p_name, or $p_name itself
*/
function constant_replace( $p_name ) {
$t_result = $p_name;
if ( is_string( $p_name ) && defined( $p_name ) ) {
// we have a constant
$t_result = constant( $p_name );
if( is_string( $p_name ) && defined( $p_name ) ) {
# we have a constant
return constant( $p_name );
}
return $t_result;
return $p_name;
}
39 changes: 27 additions & 12 deletions config_defaults_inc.php
Expand Up @@ -2803,28 +2803,36 @@
*****************************/

/**
* --- cookie path ---------------
* set this to something more restrictive if needed
* http://www.php.net/manual/en/function.setcookie.php
* Specifies the path under which a cookie is visible
* All scripts in this directory and its sub-directories will be able
* to access MantisBT cookies.
* It is recommended to set this to the actual MantisBT path.
* @link http://php.net/function.setcookie
* @global string $g_cookie_path
*/
$g_cookie_path = '/';

/**
*
* The domain that the MantisBT cookies are available to
* @global string $g_cookie_domain
*/
$g_cookie_domain = '';

/**
* cookie version for view_all_page
* Version of the view_all_page cookie
* It is not expected for the user to need to change this setting
* @see $g_view_all_cookie
* @global string $g_cookie_version
*/
$g_cookie_version = 'v8';

/**
* --- cookie prefix ---------------
* set this to a unique identifier. No spaces or periods.
* Prefix for all MantisBT cookies
* This should be an identifier which does not include spaces or periods,
* and should be unique per MantisBT installation, especially if
* $g_cookie_path is not restricting the cookies' scope to the actual
* MantisBT directory.
* @see $g_cookie_path
* @global string $g_cookie_prefix
*/
$g_cookie_prefix = 'MANTIS';
Expand All @@ -2848,10 +2856,16 @@
$g_view_all_cookie = '%cookie_prefix%_VIEW_ALL_COOKIE';

/**
*
* @global string $g_manage_cookie
* Stores the filter criteria for the Manage User page
* @global string $g_manage_users_cookie
*/
$g_manage_users_cookie = '%cookie_prefix%_MANAGE_USERS_COOKIE';

/**
* Stores the filter criteria for the Manage Config Report page
* @global string $g_manage_config_cookie
*/
$g_manage_cookie = '%cookie_prefix%_MANAGE_COOKIE';
$g_manage_config_cookie = '%cookie_prefix%_MANAGE_CONFIG_COOKIE';

/**
*
Expand Down Expand Up @@ -3915,8 +3929,9 @@
'absolute_path_default_upload_folder', 'ldap_simulation_file_path', 'cookie_path',
'plugin_path', 'db_table_prefix', 'db_table_suffix', 'db_table', 'allow_permanent_cookie',
'cookie_time_length', 'cookie_domain', 'cookie_version', 'cookie_prefix',
'string_cookie', 'project_cookie', 'view_all_cookie', 'manage_cookie', 'logout_cookie',
'bug_list_cookie', 'db_username', 'db_password', 'db_schema', 'db_type', 'hostname',
'string_cookie', 'project_cookie', 'view_all_cookie', 'manage_users_cookie',
'manage_config_cookie', 'logout_cookie', 'bug_list_cookie',
'db_username', 'db_password', 'db_schema', 'db_type', 'hostname',
'allow_signup', 'database_name', 'show_queries_count', 'show_queries_threshold',
'show_queries_list', 'admin_checks', 'version_suffix', 'global_settings', 'use_iis',
'default_language', 'language_choices_arr', 'language_auto_map', 'fallback_language',
Expand Down
1 change: 1 addition & 0 deletions core/constant_inc.php
Expand Up @@ -469,6 +469,7 @@
define( 'TOKEN_EXPIRY_COLLAPSE', 365 * 24 * 60 * 60 );

# config types
define( 'CONFIG_TYPE_DEFAULT', 0 );
define( 'CONFIG_TYPE_INT', 1 );
define( 'CONFIG_TYPE_STRING', 2 );
define( 'CONFIG_TYPE_COMPLEX', 3 );
Expand Down
16 changes: 11 additions & 5 deletions core/form_api.php
Expand Up @@ -77,20 +77,26 @@ function form_security_token( $p_form_name ) {

/**
* Get a hidden form element containing a generated form security token.
* @param string Form name
* @param string $p_form_name Form name
* @param string $p_security_token Optional security token, previously generated for the same form
* @return string Hidden form element to output
*/
function form_security_field( $p_form_name ) {
function form_security_field( $p_form_name, $p_security_token = null ) {
if ( PHP_CLI == php_mode() || OFF == config_get_global( 'form_security_validation' ) ) {
return '';
}

$t_string = form_security_token( $p_form_name );
if( is_null( $p_security_token ) ) {
$p_security_token = form_security_token( $p_form_name );
}

# Create the form element HTML string for the security token
$t_form_token = $p_form_name . '_token';
$t_element = '<input type="hidden" name="%s" value="%s"/>';
$t_element = sprintf( $t_element, $t_form_token, $t_string );
$t_element = sprintf(
'<input type="hidden" name="%s" value="%s"/>',
$t_form_token,
$p_security_token
);

return $t_element;
}
Expand Down
3 changes: 2 additions & 1 deletion core/helper_api.php
Expand Up @@ -281,7 +281,8 @@ function helper_set_current_project( $p_project_id ) {
*/
function helper_clear_pref_cookies() {
gpc_clear_cookie( config_get( 'project_cookie' ) );
gpc_clear_cookie( config_get( 'manage_cookie' ) );
gpc_clear_cookie( config_get( 'manage_users_cookie' ) );
gpc_clear_cookie( config_get( 'manage_config_cookie' ) );
}

/**
Expand Down
3 changes: 3 additions & 0 deletions core/obsolete.php
Expand Up @@ -142,3 +142,6 @@
# changes in 1.2.8
config_obsolete( 'show_attachment_indicator' );
config_obsolete( 'default_avatar', '' );

# changes in 1.2.13
config_obsolete( 'manage_cookie', 'manage_users_cookie' );

0 comments on commit e539dd6

Please sign in to comment.