Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: mantisbt/mantisbt
base: b8f39bcd85f3
...
head fork: mantisbt/mantisbt
compare: 0719f32dd9fe
Checking mergeability… Don't worry, you can still create the pull request.
  • 5 commits
  • 12 files changed
  • 0 commit comments
  • 1 contributor
Commits on Mar 08, 2013
@dregad dregad Manage config page: added filtering
Porting the following 1.2.x commits
- f8a81a3
- 259f95c
- 3f75f68
- 9f72490
- efdd6a7
- 9dbfcd7
- beea901
- 65696fb
- b6f03b7
- 8b426cf
- d76a210
- c61dc63

Issue #14559, #15415
0c81929
@dregad dregad Make it possible to edit config options in adm_config_report.php
Use CONFIG_TYPE_xxx constants instead of magic strings to define the
type of config value to process.

Added code for FLOAT type which was previously handled through COMPLEX.

Improve handling of INT (and FLOAT) by calling constant_replace(),
allowing user to specify a defined constant instead of a numeric value.

The 'Username', 'Project Name' and 'Configuration Option' fields in the
'Set Configuration Option' form are preset to the corresponding value
from the filter or defaulting to ALL_USERS, ALL_PROJECTS and blank
respectively if the filter is not defined or set to '[any]'. This allows
easier definition of related config, e.g. for a given project or user.

Port of 1.2.x commits
- 8890b21
- 8b426cf
- 5858a65

Fixes #7586, #15416
57f5740
@dregad dregad Fix performance issue on adm_config_report.php
In systems with large numbers of config items in mantis_config_table, the
Configuration Report page can take a very long time to load.

This behavior is due to each of the 'Delete' buttons being printed with
its own form, each one having a security token. The performance
bottleneck is actually the serialize/unserialize calls executed while
storing/retrieving the token from the PHP session.

To avoid this problem, the print_button() and form_security_field()
functions have been modified to accept a security token as an optional
parameter. This allows the calling page to generate a single token,
which is shared by all buttons.

Furthermore, print_button() also allows the security token parameter to
be 'OFF', which prevents the function from displaying a security field.
This is useful for buttons not resulting in modifications (i.e. not
requiring CSRF protection).

Fixes #13680
5c3ac41
@dregad dregad Support for multi-dimentional arrays in admin_config_set.php
This commit adds support for multi-dimentional arrays, as well as
correct handling of commas and '=>' within strings.

This is based on work by jspezeski; the original code was modified to
align with MantisBT coding guidelines, simplification of the recursive
function and fixing a couple of errors in regex.

Fixes #13298
8b98640
@dregad dregad admin_config_set: Revised process_complex_value() function
The new code features an improved regex, which deals more efficiently
with the parsing of multi-dimensional and associative arrays.

Regex are defined as static variables for better performance with
repeated and recursive calls

Known issue: an invalid definition like 'array(array(1,2)=>array(3,4))'
is not properly parsed.

Fixes #13298
0719f32
View
394 adm_config_report.php
@@ -63,35 +63,41 @@
print_manage_menu( 'adm_config_report.php' );
print_manage_config_menu( 'adm_config_report.php' );
+$t_config_types = array(
+ CONFIG_TYPE_DEFAULT => 'default',
+ CONFIG_TYPE_INT => 'integer',
+ CONFIG_TYPE_FLOAT => 'float',
+ CONFIG_TYPE_COMPLEX => 'complex',
+ CONFIG_TYPE_STRING => 'string',
+);
+
function get_config_type( $p_type ) {
- switch( $p_type ) {
- case CONFIG_TYPE_INT:
- return "integer";
- case CONFIG_TYPE_FLOAT:
- return "float";
- case CONFIG_TYPE_COMPLEX:
- return "complex";
- case CONFIG_TYPE_STRING:
- default:
- return "string";
+ global $t_config_types;
+ if( array_key_exists( $p_type, $t_config_types ) ) {
+ return $t_config_types[$p_type];
+ } else {
+ return $t_config_types[CONFIG_TYPE_DEFAULT];
}
}
-function print_config_value_as_string( $p_type, $p_value ) {
+function print_config_value_as_string( $p_type, $p_value, $p_for_display = true ) {
$t_corrupted = false;
switch( $p_type ) {
+ case CONFIG_TYPE_DEFAULT:
+ return;
case CONFIG_TYPE_FLOAT:
- $t_value = (float)$p_value;
- echo $t_value;
+ echo (float)$p_value;
return;
case CONFIG_TYPE_INT:
- $t_value = (integer)$p_value;
- echo $t_value;
+ echo (integer)$p_value;
return;
case CONFIG_TYPE_STRING:
- $t_value = config_eval( $p_value );
- echo string_nl2br( string_html_specialchars( $t_value ) );
+ $t_value = string_nl2br( string_html_specialchars( config_eval( $p_value ) ) );
+ if( $p_for_display ) {
+ $t_value = '<p id="adm-config-value">' . "'$t_value'" . '</p>';
+ }
+ echo $t_value;
return;
case CONFIG_TYPE_COMPLEX:
$t_value = @unserialize( $p_value );
@@ -104,114 +110,384 @@ function print_config_value_as_string( $p_type, $p_value ) {
break;
}
- echo '<pre>';
-
if ( $t_corrupted ) {
- echo lang_get( 'configuration_corrupted' );
+ $t_output = $p_for_display ? lang_get( 'configuration_corrupted' ) : '';
+ } else {
+ $t_output = var_export( $t_value, true );
+ }
+
+ if( $p_for_display ) {
+ echo '<pre id="adm-config-value">' . string_attribute( $t_output ) . '</pre>';
} else {
- if ( function_exists( 'var_export' ) ) {
- var_export( $t_value );
- } else {
- print_r( $t_value );
+ echo $t_output;
+ }
+}
+
+function print_option_list_from_array( $p_array, $p_filter_value ) {
+ foreach( $p_array as $t_key => $t_value ) {
+ echo "<option value='$t_key'";
+ check_selected( (string)$p_filter_value, (string)$t_key );
+ echo '>' . string_attribute( $t_value ) . "</option>\n";
+ }
+}
+
+# Get filter values
+$t_filter_save = gpc_get_bool( 'save' );
+$t_filter_default = gpc_get_bool( 'default_filter_button', false );
+$t_filter_reset = gpc_get_bool( 'reset_filter_button', false );
+if( $t_filter_default ) {
+ $t_filter_user_value = ALL_USERS;
+ $t_filter_project_value = ALL_PROJECTS;
+ $t_filter_config_value = META_FILTER_NONE;
+} else if( $t_filter_reset ) {
+ $t_filter_user_value = META_FILTER_NONE;
+ $t_filter_project_value = META_FILTER_NONE;
+ $t_filter_config_value = META_FILTER_NONE;
+} else {
+ $t_filter_user_value = gpc_get_int( 'filter_user_id', ALL_USERS );
+ $t_filter_project_value = gpc_get_int( 'filter_project_id', ALL_PROJECTS );
+ $t_filter_config_value = gpc_get_string( 'filter_config_id', META_FILTER_NONE );
+}
+
+# Manage filter's persistency through cookie
+$t_cookie_name = config_get( 'manage_config_cookie' );
+if( $t_filter_save ) {
+ # Save user's filter to the cookie
+ $t_cookie_string = implode(
+ ':',
+ array(
+ $t_filter_user_value,
+ $t_filter_project_value,
+ $t_filter_config_value,
+ )
+ );
+ gpc_set_cookie( $t_cookie_name, $t_cookie_string, true );
+} else {
+ # Retrieve the filter from the cookie if it exists
+ $t_cookie_string = gpc_get_cookie( $t_cookie_name, null );
+ if( null !== $t_cookie_string ) {
+ $t_cookie_contents = explode( ':', $t_cookie_string );
+
+ $t_filter_user_value = $t_cookie_contents[0];
+ $t_filter_project_value = $t_cookie_contents[1];
+ $t_filter_config_value = $t_cookie_contents[2];
+
+ if( $t_filter_project_value != META_FILTER_NONE && project_exists( $t_filter_project_value ) ) {
+ $t_filter_project_value = ALL_PROJECTS;
}
}
+}
+
+# Get config edit values
+$t_edit_user_id = gpc_get_int( 'user_id', $t_filter_user_value == META_FILTER_NONE ? ALL_USERS : $t_filter_user_value );
+$t_edit_project_id = gpc_get_int( 'project_id', $t_filter_project_value == META_FILTER_NONE ? ALL_PROJECTS : $t_filter_project_value );
+$t_edit_option = gpc_get_string( 'config_option', $t_filter_config_value == META_FILTER_NONE ? '' : $t_filter_config_value );
+$t_edit_type = gpc_get_string( 'type', CONFIG_TYPE_DEFAULT );
+$t_edit_value = gpc_get_string( 'value', '' );
+
+# Apply filters
+$t_config_table = db_get_table( 'config' );
+$t_project_table = db_get_table( 'project' );
- echo '</pre>';
+# Get users in db having specific configs
+$query = "SELECT DISTINCT user_id
+ FROM $t_config_table
+ WHERE user_id <> " . db_param() ;
+$t_result = db_query_bound( $query, array( ALL_USERS ) );
+if( $t_filter_user_value != META_FILTER_NONE && $t_filter_user_value != ALL_USERS ) {
+ # Make sure the filter value exists in the list
+ $t_users_list[$t_filter_user_value] = user_get_name( $t_filter_user_value );
+} else {
+ $t_users_list = array();
+}
+while ( $row = db_fetch_array( $t_result ) ) {
+ $t_user_id = $row['user_id'];
+ $t_users_list[$t_user_id] = user_get_name( $t_user_id );
+}
+asort( $t_users_list );
+# Prepend '[any]' and 'All Users' to the list
+$t_users_list = array(
+ META_FILTER_NONE => '[' . lang_get( 'any' ) . ']',
+ ALL_USERS => lang_get( 'all_users' ),
+ )
+ + $t_users_list;
+
+# Get projects in db with specific configs
+$query = "SELECT DISTINCT project_id, pt.name as project_name
+ FROM $t_config_table as ct
+ JOIN $t_project_table as pt ON pt.id = ct.project_id
+ WHERE project_id!=0
+ ORDER BY project_name";
+$t_result = db_query_bound( $query );
+$t_projects_list[META_FILTER_NONE] = '[' . lang_get( 'any' ) . ']';
+$t_projects_list[ALL_PROJECTS] = lang_get( 'all_projects' );
+while ( $row = db_fetch_array( $t_result ) ) {
+ extract( $row, EXTR_PREFIX_ALL, 'v' );
+ $t_projects_list[$v_project_id] = $v_project_name;
+}
+
+# Get config list used in db
+$query = "SELECT DISTINCT config_id
+ FROM $t_config_table
+ ORDER BY config_id";
+$t_result = db_query_bound( $query );
+$t_configs_list[META_FILTER_NONE] = '[' . lang_get( 'any' ) . ']';
+if( $t_filter_config_value != META_FILTER_NONE ) {
+ # Make sure the filter value exists in the list
+ $t_configs_list[$t_filter_config_value] = $t_filter_config_value;
+}
+while ( $row = db_fetch_array( $t_result ) ) {
+ extract( $row, EXTR_PREFIX_ALL, 'v' );
+ $t_configs_list[$v_config_id] = $v_config_id;
}
-$t_config_table = db_get_table( 'config' );
-$query = "SELECT config_id, user_id, project_id, type, value, access_reqd FROM $t_config_table ORDER BY user_id, project_id, config_id";
+# Build filter's where clause
+$t_where = '';
+if( $t_filter_user_value != META_FILTER_NONE ) {
+ $t_where .= " AND user_id = $t_filter_user_value ";
+}
+if( $t_filter_project_value != META_FILTER_NONE ) {
+ $t_where .= " AND project_id = $t_filter_project_value ";
+}
+if( $t_filter_config_value != META_FILTER_NONE ) {
+ $t_where .= " AND config_id = '$t_filter_config_value' ";
+}
+if( $t_where != '' ) {
+ $t_where = " WHERE 1=1 " . $t_where;
+}
+
+$query = "SELECT config_id, user_id, project_id, type, value, access_reqd
+ FROM $t_config_table
+ $t_where
+ ORDER BY user_id, project_id, config_id ";
$result = db_query_bound( $query );
?>
-<div id="adm-config-div" class="table-container">
+
+<!-- FILTER FORM -->
+<div id="config-filter-div" class="table-container">
+
+ <form name="filter_form" method="post">
+ <?php # CSRF protection not required here - form does not result in modifications ?>
+ <input type="hidden" name="save" value="1" />
+
+ <table cellspacing="1">
+ <!-- Title -->
+ <tr>
+ <td class="form-title" colspan="7">
+ <?php echo lang_get( 'filters' ) ?>
+ </td>
+ </tr>
+
+ <tr class="row-category2">
+ <td class="small-caption">
+ <?php echo lang_get( 'username' ); ?>: <br />
+ </td>
+ <td class="small-caption">
+ <?php echo lang_get( 'project_name' ); ?>: <br />
+ </td>
+ <td class="small-caption">
+ <?php echo lang_get( 'configuration_option' ); ?>: <br />
+ </td>
+ </tr>
+
+ <tr class="row-1">
+ <td>
+ <select name="filter_user_id">
+ <?php
+ print_option_list_from_array( $t_users_list, $t_filter_user_value );
+ ?>
+ </select>
+ </td>
+ <td>
+ <select name="filter_project_id">
+ <?php
+ print_option_list_from_array( $t_projects_list, $t_filter_project_value );
+ ?>
+ </select>
+ </td>
+ <td>
+ <select name="filter_config_id">
+ <?php
+ print_option_list_from_array( $t_configs_list, $t_filter_config_value );
+ ?>
+ </select>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="3">
+ <input name="apply_filter_button" type="submit" class="button-small" value="<?php echo lang_get( 'filter_button' )?>" />
+ <input name="default_filter_button" type="submit" class="button-small" value="<?php echo lang_get( 'default_filter' )?>" />
+ <input name="reset_filter_button" type="submit" class="button-small" value="<?php echo lang_get( 'reset_query' )?>" />
+ </td>
+ </tr>
+ </table>
+ </form>
+</div>
+
+<div>
+<div id="adm-config-div" class="table-container" style="display: table">
<h2><?php echo lang_get( 'database_configuration' ) ?></h2>
- <table cellspacing="1" cellpadding="5" border="1">
+<!-- <table cellspacing="1" cellpadding="5" border="1"> -->
+ <table cellspacing="1" width="100%">
<tr class="row-category">
- <th class="center"><?php echo lang_get( 'username' ) ?></th>
- <th class="center"><?php echo lang_get( 'project_name' ) ?></th>
+ <th><?php echo lang_get( 'username' ) ?></th>
+ <th><?php echo lang_get( 'project_name' ) ?></th>
<th><?php echo lang_get( 'configuration_option' ) ?></th>
- <th class="center"><?php echo lang_get( 'configuration_option_type' ) ?></th>
- <th class="center"><?php echo lang_get( 'configuration_option_value' ) ?></th>
- <th class="center"><?php echo lang_get( 'access_level' ) ?></th>
+ <th><?php echo lang_get( 'configuration_option_type' ) ?></th>
+ <th><?php echo lang_get( 'configuration_option_value' ) ?></th>
+ <th><?php echo lang_get( 'access_level' ) ?></th>
<?php if ( $t_read_write_access ): ?>
- <th class="center"><?php echo lang_get( 'actions' ) ?></th>
+ <th><?php echo lang_get( 'actions' ) ?></th>
<?php endif; ?>
- </tr><?php
+ </tr>
+<?php
+ # Pre-generate a form security token to avoid performance issues when the
+ # db contains a large number of configurations
+ $t_form_security_token = form_security_token( 'adm_config_delete' );
+
while ( $row = db_fetch_array( $result ) ) {
- extract( $row, EXTR_PREFIX_ALL, 'v' ); ?>
- <tr <?php echo helper_alternate_class() ?>>
- <td class="center">
+ extract( $row, EXTR_PREFIX_ALL, 'v' );
+
+?>
+<!-- Repeated Info Rows -->
+ <tr <?php echo helper_alternate_class() ?> width="100%">
+ <td >
<?php echo ($v_user_id == 0) ? lang_get( 'all_users' ) : string_display_line( user_get_name( $v_user_id ) ) ?>
</td>
- <td class="center"><?php echo string_display_line( project_get_name( $v_project_id, false ) ) ?></td>
+ <td ><?php echo string_display_line( project_get_name( $v_project_id, false ) ) ?></td>
<td><?php echo string_display_line( $v_config_id ) ?></td>
- <td class="center"><?php echo string_display_line( get_config_type( $v_type ) ) ?></td>
- <td class="left"><?php print_config_value_as_string( $v_type, $v_value ) ?></td>
- <td class="center"><?php echo get_enum_element( 'access_levels', $v_access_reqd ) ?></td>
+ <td ><?php echo string_display_line( get_config_type( $v_type ) ) ?></td>
+ <td style="overflow-x:auto;"><?php print_config_value_as_string( $v_type, $v_value ) ?></td>
+ <td ><?php echo get_enum_element( 'access_levels', $v_access_reqd ) ?></td>
<?php if ( $t_read_write_access ): ?>
- <td class="center">
+ <td >
<?php
- if ( config_can_delete( $v_config_id ) ) {
- print_button( "adm_config_delete.php?user_id=$v_user_id&project_id=$v_project_id&config_option=$v_config_id", lang_get( 'delete_link' ) );
+ if (
+ config_can_delete( $v_config_id )
+ && access_has_global_level( config_get( 'set_configuration_threshold' ) )
+ ) {
+ # Update button (will populate edit form at page bottom)
+ print_button(
+ '#config_set_form',
+ lang_get( 'edit_link' ),
+ array(
+ 'filter_user_id' => $t_filter_user_value,
+ 'filter_project_id' => $t_filter_project_value,
+ 'filter_config_id' => $t_filter_config_value,
+ 'user_id' => $v_user_id,
+ 'project_id' => $v_project_id,
+ 'config_option' => $v_config_id,
+ 'type' => $v_type,
+ 'value' => $v_value,
+ ),
+ OFF
+ );
+
+ # Delete button
+ print_button(
+ 'adm_config_delete.php',
+ lang_get( 'delete_link' ),
+ array(
+ 'user_id' => $v_user_id,
+ 'project_id' => $v_project_id,
+ 'config_option' => $v_config_id,
+ ),
+ $t_form_security_token
+ );
} else {
echo '&#160;';
}
?>
</td>
<?php endif; ?>
- </tr><?php
- } # end for loop ?>
+ </tr>
+<?php
+ } # end for loop
+?>
</table>
-</div><?php
+</div>
+</div>
+<?php
+# Only display the edit form if user is authorized to change configuration
if ( $t_read_write_access ) { ?>
-<div class="form-container">
- <form method="post" action="adm_config_set.php">
+<div id="config-edit-div" class="form-container">
+ <!-- Config Set Form -->
+ <form id="config_set_form" method="post" action="adm_config_set.php">
<fieldset>
<legend><span><?php echo lang_get( 'set_configuration_option' ) ?></span></legend>
<?php echo form_security_field( 'adm_config_set' ) ?>
+
+ <!-- Username -->
<div class="field-container <?php echo helper_alternate_class_no_attribute(); ?>">
<label for="config-user-id"><span><?php echo lang_get( 'username' ) ?></span></label>
<span class="select">
<select id="config-user-id" name="user_id">
- <option value="0" selected="selected"><?php echo lang_get( 'all_users' ); ?></option>
+ <option value="<?php echo ALL_USERS; ?>"
+ <?php check_selected( $t_edit_user_id, ALL_USERS ) ?>>
+ <?php echo lang_get( 'all_users' ); ?>
+ </option>
<?php print_user_option_list( 0 ) ?>
</select>
</span>
<span class="label-style"></span>
</div>
+
+ <!-- Project -->
<div class="field-container <?php echo helper_alternate_class_no_attribute(); ?>">
<label for="config-project-id"><span><?php echo lang_get( 'project_name' ) ?></span></label>
<span class="select">
<select id="config-project-id" name="project_id">
- <option value="0" selected="selected"><?php echo lang_get( 'all_projects' ); ?></option>
+ <option value="<?php echo ALL_PROJECTS; ?>"
+ <?php check_selected( $t_edit_project_id, ALL_PROJECTS ); ?>>
+ <?php echo lang_get( 'all_projects' ); ?>
+ </option>
<?php print_project_option_list( ALL_PROJECTS, false ) ?>
</select>
</span>
<span class="label-style"></span>
</div>
+
+ <!-- Config option name -->
<div class="field-container <?php echo helper_alternate_class_no_attribute(); ?>">
<label for="config-option"><span><?php echo lang_get( 'configuration_option' ) ?></span></label>
- <span class="input"><input type="text" id="config-option" name="config_option" value="" size="64" maxlength="64" /></span>
+ <span class="input">
+ <input type="text" name="config_option"
+ value="<?php echo $t_edit_option; ?>"
+ size="64" maxlength="64" />
+ </span>
<span class="label-style"></span>
</div>
+
+ <!-- Option type -->
<div class="field-container <?php echo helper_alternate_class_no_attribute(); ?>">
<label for="config-type"><span><?php echo lang_get( 'configuration_option_type' ) ?></span></label>
<span class="select">
<select id="config-type" name="type">
- <option value="default" selected="selected">default</option>
- <option value="string">string</option>
- <option value="integer">integer</option>
- <option value="complex">complex</option>
+ <?php
+ foreach( $t_config_types as $t_key => $t_type ) {
+ echo '<option value="' . $t_key . '" ';
+ check_selected( $t_key, $t_edit_type );
+ echo ">$t_type</option>";
+ }
+ ?>
</select>
</span>
<span class="label-style"></span>
</div>
+
+ <!-- Option Value -->
<div class="field-container <?php echo helper_alternate_class_no_attribute(); ?>">
<label for="config-value"><span><?php echo lang_get( 'configuration_option_value' ) ?></span></label>
- <span class="textarea"><textarea id="config-value" name="value" cols="80" rows="10"></textarea></span>
+ <span class="textarea">
+ <textarea name="value" cols="80" rows="10"><?php
+ echo print_config_value_as_string( $t_edit_type, $t_edit_value, false );
+ ?></textarea>
+ </span>
<span class="label-style"></span>
</div>
+
+ <!-- Submit button -->
<span class="submit-button"><input type="submit" name="config_set" class="button" value="<?php echo lang_get( 'set_configuration_option' ) ?>" /></span>
</fieldset>
</form>
View
198 adm_config_set.php
@@ -80,52 +80,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';
+ $t_type = CONFIG_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';
+ $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 );
@@ -136,13 +122,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 );
+ # we have a constant
+ return constant( $p_name );
}
- return $t_result;
+ return $p_name;
}
View
39 config_defaults_inc.php
@@ -3068,28 +3068,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';
@@ -3113,10 +3121,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';
/**
*
@@ -4144,8 +4158,9 @@
'anonymous_account', 'compress_html', 'content_expire', 'allow_permanent_cookie',
'cookie_time_length', 'cookie_path', 'cookie_domain', 'cookie_version',
'cookie_prefix', 'string_cookie', 'project_cookie', 'view_all_cookie',
- 'manage_cookie', 'logout_cookie', 'bug_list_cookie', 'crypto_master_salt',
- 'custom_headers', 'database_name', 'db_username', 'db_password', 'db_schema', 'db_type',
+ 'manage_config_cookie', 'manage_user_cookie', 'logout_cookie',
+ 'bug_list_cookie', 'crypto_master_salt', 'custom_headers',
+ 'database_name', 'db_username', 'db_password', 'db_schema', 'db_type',
'db_table_prefix','db_table_suffix', 'display_errors', 'form_security_validation',
'hostname','html_valid_tags', 'html_valid_tags_single_line', 'default_language',
'language_auto_map', 'fallback_language', 'login_method', 'plugins_enabled', 'session_handler',
View
1  core/constant_inc.php
@@ -475,6 +475,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 );
View
16 core/form_api.php
@@ -81,20 +81,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;
}
View
3  core/helper_api.php
@@ -413,7 +413,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' ) );
}
/**
View
3  core/obsolete.php
@@ -143,6 +143,9 @@
config_obsolete( 'show_attachment_indicator' );
config_obsolete( 'default_avatar', '' );
+# changes in 1.2.13
+config_obsolete( 'manage_cookie', 'manage_users_cookie' );
+
# changes in 1.3.0dev
config_obsolete( 'bugnote_allow_user_edit_delete', '' );
config_obsolete( 'password_confirm_hash_magic_string', 'crypto_master_salt' );
View
28 core/print_api.php
@@ -1161,23 +1161,37 @@ function print_manage_project_sort_link( $p_page, $p_string, $p_field, $p_dir, $
print_link( "$p_page?sort=$t_field&dir=$t_dir", $p_string );
}
-# print a button which presents a standalone form.
-# $p_action_page - The action page
-# $p_label - The button label
-# $p_args_to_post - An associative array with key => value to be posted, can be null.
-function print_button( $p_action_page, $p_label, $p_args_to_post = null ) {
+/**
+ * Print a button which presents a standalone form.
+ * If $p_security_token is OFF, the button's form will not contain a security
+ * field; this is useful when form does not result in modifications (CSRF is not
+ * needed). If otherwise specified (i.e. not null), the parameter must contain
+ * a valid security token, previously generated by form_security_token().
+ * Use this to avoid performance issues when loading pages having many calls to
+ * this function, such as adm_config_report.php.
+ * @param string $p_action_page The action page
+ * @param string $p_label The button label
+ * @param array $p_args_to_post Associative array of arguments to be posted, with
+ * arg name => value, defaults to null (no args)
+ * @param mixed $p_security_token Optional; null (default), OFF or security token string
+ * @see form_security_token()
+ */
+function print_button( $p_action_page, $p_label, $p_args_to_post = null, $p_security_token = null ) {
$t_form_name = explode( '.php', $p_action_page, 2 );
# TODO: ensure all uses of print_button supply arguments via $p_args_to_post (POST)
# instead of via $p_action_page (GET). Then only add the CSRF form token if
# arguments are being sent via the POST method.
echo '<form method="post" action="', htmlspecialchars( $p_action_page ), '" class="action-button">';
echo '<fieldset>';
- echo form_security_field( $t_form_name[0] );
+ if( $p_security_token !== OFF ) {
+ echo form_security_field( $t_form_name[0], $p_security_token );
+ }
echo '<input type="submit" class="button-small" value="', $p_label, '" />';
if( $p_args_to_post !== null ) {
foreach( $p_args_to_post as $t_var => $t_value ) {
- echo "<input type=\"hidden\" name=\"$t_var\" value=\"$t_value\" />";
+ echo '<input type="hidden" name="' . $t_var .
+ '" value="' . htmlentities( $t_value ) . '" />';
}
}
View
11 css/default.css
@@ -454,7 +454,16 @@ div#account-profile-update-div {
width: 65%;
}
div#adm-config-div {
- width: 95%;
+ max-width: 95%;
+ display: table;
+}
+p#adm-config-value {
+ max-width: 400px;
+ word-wrap: break-word;
+}
+pre#adm-config-value {
+ max-width: 400px;
+ overflow: auto
}
div#project-delete-div {
text-align: center;
View
78 docbook/Admin_Guide/en-US/Configuration.xml
@@ -2595,38 +2595,38 @@
<section id="admin.config.cookies">
<title>Cookies</title>
- <para>The configuration variables $g_string_cookie,
- $g_project_cookie, $g_view_all_cookie, $g_manage_cookie are
- calculated based on $g_cookie_prefix. When you change the cookie
- prefix in config_inc.php, you need to follow it with a copy of the
- four lines that calculate the names for these cookies.
- </para>
<variablelist>
<varlistentry>
<term>$g_cookie_path</term>
<listitem>
- <para>This is specifies to the path under which a cookie is
- visible. All scripts in this directory and its sub-directories will
- be able to access MantisBT cookies. Default value is '/'. It is
- recommended to set this to the actual MantisBT path.
+ <para>Specifies the path under which a cookie is visible.
+ </para>
+ <para>All scripts in this directory and its sub-directories
+ will be able to access MantisBT cookies.
+ </para>
+ <para>Default value is '/'.
+ It is recommended to set this to the actual MantisBT path.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>$g_cookie_domain</term>
<listitem>
- <para>Unused</para>
+ <para>The domain that the MantisBT cookies are available to.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>$g_cookie_version</term>
<listitem>
- <para>Cookie version is used as a prefix for cookies that should
- be expire when the code is changed in a certain way. The developers
- would increase this version when necessary, which in effect will
- cause the creation of new cookies that are compatible with the new
- code. It is not expected for the user to need to change this
+ <para>Version of the view_all_page cookie.</para>
+ <para>This is used as a prefix for cookies that should
+ be expired when the code is changed in a certain way.
+ The developers would increase this version when necessary,
+ which in effect will cause the creation of new cookies
+ that are compatible with the new code.
+ </para>
+ <para>It is not expected for the user to need to change this
setting.
</para>
</listitem>
@@ -2634,12 +2634,48 @@
<varlistentry>
<term>$g_cookie_prefix</term>
<listitem>
- <para>This should be set to a unique identifier which does not
- include spaces or periods. Again, this should be unique per
- MantisBT installation, especially if $g_cookie_path is not
- restricting the cookies scope to the actual MantisBT
- directory.
+ <para>Prefix for all MantisBT cookies</para>
+ <para>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.
</para>
+ <para>It applies to the cookies listed below. Their actual
+ names are calculated by prepending the prefix, and
+ it is not expected for the user to need to change these.
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>$g_string_cookie</para>
+ </listitem>
+ <listitem>
+ <para>$g_project_cookie</para>
+ </listitem>
+ <listitem>
+ <para>$g_view_all_cookie</para>
+ </listitem>
+ <listitem>
+ <para>$g_manage_users_cookie</para>
+ <para>Stores the filter criteria for the
+ Manage Users page
+ </para>
+ </listitem>
+ <listitem>
+ <para>$g_manage_config_cookie</para>
+ <para>Stores the filter criteria for the
+ Manage Config Report page
+ </para>
+ </listitem>
+ <listitem>
+ <para>$g_logout_cookie</para>
+ </listitem>
+ <listitem>
+ <para>$g_bug_list_cookie</para>
+ </listitem>
+ </itemizedlist>
+
</listitem>
</varlistentry>
</variablelist>
View
3  lang/strings_english.txt
@@ -660,6 +660,7 @@ If you requested this verification, visit the following URL to change your passw
# manage_page.php
'hide_disabled' => 'Hide Disabled',
'filter_button' => 'Apply Filter',
+ 'default_filter' => 'Default Filter',
'create_filter_link' => 'Create Permalink',
'create_short_link' => 'Create Short Link',
'filter_permalink' => 'Following is a permanent link to the currently configured filter:',
@@ -1350,7 +1351,7 @@ If you requested this verification, visit the following URL to change your passw
'delete_attachment_button' => 'Delete',
'delete_attachment_sure_msg' => 'Are you sure you wish to delete this attachment?',
'upload_issue_attachments' => 'Upload issue attachments',
- 'filters' => 'filters',
+ 'filters' => 'Filters',
'save_filters' => 'Save filters',
'save_filters_as_shared' => 'Save filters as shared',
'use_saved_filters' => 'Use saved filters',
View
2  manage_user_page.php
@@ -67,7 +67,7 @@
$f_page_number = gpc_get_int( 'page_number', 1 );
$t_user_table = db_get_table( 'user' );
-$t_cookie_name = config_get( 'manage_cookie' );
+$t_cookie_name = config_get( 'manage_users_cookie' );
$t_lock_image = '<img src="' . config_get( 'icon_path' ) . 'protected.gif" width="8" height="15" alt="' . lang_get( 'protected' ) . '" />';
$c_filter = '';

No commit comments for this range

Something went wrong with that request. Please try again.