Skip to content

Comparing changes

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

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also .
...
  • 18 commits
  • 13 files changed
  • 2 commit comments
  • 2 contributors
Commits on Dec 14, 2012
Lapinkiller fix #14559 - Filter for adm_report_config.php
Signed-off-by: Damien Regad <damien.regad@merckgroup.com>
f8a81a3
@dregad dregad Issue #14559: improve adm_report_config.php filter
The following changes were made

- revised UI to make it more similar to the issues filter
- make use of existing constants and language strings
- filter defaults to All Users / All Projects / All options which avoids
  performance issues in installations having a large number of entries
  in the config table (workaround for issue #13680)
- filter form uses post instead of get method
- comply to coding guidelines
259f95c
Commits on Dec 28, 2012
@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.

Fixes #7586
8890b21
Commits on Dec 31, 2012
@dregad dregad Revised Cookies documentation
- Removed paragraph with incorrect information about need to redefine
  cookie variables after modifying $g_cookie_prefix.
- Revised layout
- Updated description for some variables
- Reflected these changes in related comments in config_default_inc.php
3f75f68
@dregad dregad Fix 1st uppercase letter for 'Filters' language string
Affects issue #14559
9f72490
@dregad dregad Respect $g_show_realname setting in config report's filter userlist
The filter's original implementation displayed "Realname (username)" in
the user selection list. We now only retrieve the user id from the db,
and call user_get_name() to get either the realname or the username as
appropriate instead.

Issue #14559
efdd6a7
@dregad dregad Manage persistency of config report filter using a cookie
A new cookie 'manage_config_cookie' was added to store the user's filter
criteria.

Includes documentation update to admin guide.

Issue #14559
9dbfcd7
@dregad dregad Rename 'manage_cookie' to 'manage_users_cookie'
After the introduction of the 'manage_config_cookie', the name of the
existing cookie ('manage_cookie') which is used to store the filter for
the Manage Users page could be misleading, so it has been renamed to
'manage_users_cookie'.
beea901
Commits on Jan 01, 2013
@dregad dregad Whitespace: remove unnecessary code indentation 65696fb
Commits on Jan 02, 2013
@dregad dregad Config report filter: added handling for invalid values in cookie
In some cases, the cookie could contain values which are no longer
applicable as filter criteria, e.g.
  - a project id which has been deleted
  - a user id for whom there are no config options
  - a config id for which there are no config options

The code now correctly handles these, by making sure that either the
filter criteria is dynamically updated to a valid value (ALL_PROJECTS
for projects), or the filter's selection list effectively includes the
invalid value (user id, config id), so that the displayed filter
reflects the actual data listed in the Database Configuration table.

Fixes the bug reported by atrol in issue #14559's bugnote 34648
b6f03b7
@dregad dregad Config report: preset the edit form to the current filter
The 'Username', 'Project Name' and 'Configuration Option' fields in the
'Set Configuration Option' form are now 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.
8b426cf
@dregad dregad Config report filter: added buttons to clear and reset default filter
This provides the user with a single-click way to

 - reset the filter to default settings
   (i.e. ALL_USERS, ALL_PROJECTS, [any] config).
 - clear the filter to display all configs
   (i.e. [any] user, [any] project, [any] config)

Issue #14559
d76a210
@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
db0e3d3
Commits on Jan 09, 2013
@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
efb8b6b
@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
d000c4d
Commits on Jan 22, 2013
@dregad dregad Merge branch 'manage-config' into master-1.2.x
This branch implements several improvements to the Manage Configuration
page, including:

 - better performance
 - filtering
 - ability to edit config options
e539dd6
@dregad dregad Updated CREDITS file in preparation of 1.2.13 release 6492038
@dregad dregad Bump version and update release notes for 1.2.13 d38abf9
View
348 adm_config_report.php
@@ -32,35 +32,42 @@
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'" ) );
+ if( $p_for_display ) {
+ $t_value = "'$t_value'";
+ }
+ echo string_nl2br( string_html_specialchars( $t_value ) );
return;
case CONFIG_TYPE_COMPLEX:
$t_value = @unserialize( $p_value );
@@ -73,27 +80,223 @@ function print_config_value_as_string( $p_type, $p_value ) {
break;
}
- echo '<pre>';
+ if( $t_corrupted ) {
+ $t_output = $p_for_display ? lang_get( 'configuration_corrupted' ) : '';
+ } else {
+ $t_output = var_export( $t_value, true );
+ }
- if ( $t_corrupted ) {
- echo lang_get( 'configuration_corrupted' );
+ if( $p_for_display ) {
+ echo "<pre>$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( $p_filter_value, $t_key );
+ echo ">$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( 'mantis_config_table' );
+ $t_project_table = db_get_table( 'mantis_project_table' );
+
+ # 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;
+ }
- echo '</pre>';
+ # 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;
}
- $t_config_table = db_get_table( 'mantis_config_table' );
- $query = "SELECT config_id, user_id, project_id, type, value, access_reqd FROM $t_config_table ORDER BY user_id, project_id, config_id";
+ $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 );
?>
+
<br />
<div align="center">
+
+<!-- FILTER FORM -->
+<form id="filter_form" method="post">
+<?php # CSRF protection not required here - form does not result in modifications ?>
+ <input type="hidden" name="save" value="1" />
+
+<table class="width100" 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>
+
+<br />
+
+<!-- CONFIGURATIONS LIST -->
<table class="width100" cellspacing="1">
<!-- Title -->
@@ -126,6 +329,10 @@ function print_config_value_as_string( $p_type, $p_value ) {
</td>
</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' );
@@ -152,8 +359,35 @@ function print_config_value_as_string( $p_type, $p_value ) {
</td>
<td class="center">
<?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(
+ '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;';
}
@@ -164,12 +398,17 @@ function print_config_value_as_string( $p_type, $p_value ) {
} # end for loop
?>
</table>
+
+
<?php
- if ( access_has_global_level( config_get('set_configuration_threshold' ) ) ) {
+ # Only display the edit form if user is authorized to change configuration
+ if ( access_has_global_level( config_get( 'set_configuration_threshold' ) ) ) {
?>
<br />
+
<!-- Config Set Form -->
-<form method="post" action="adm_config_set.php">
+
+<form id="config_set_form" method="post" action="adm_config_set.php">
<?php echo form_security_field( 'adm_config_set' ) ?>
<table class="width100" cellspacing="1">
@@ -179,67 +418,94 @@ function print_config_value_as_string( $p_type, $p_value ) {
<?php echo lang_get( 'set_configuration_option' ) ?>
</td>
</tr>
+
+<!-- Username -->
<tr <?php echo helper_alternate_class() ?> valign="top">
<td>
<?php echo lang_get( 'username' ) ?>
</td>
<td>
<select name="user_id">
- <option value="0" selected="selected"><?php echo lang_get( 'all_users' ); ?></option>
- <?php print_user_option_list( auth_get_current_user_id() ) ?>
+ <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( $t_edit_user_id ) ?>
</select>
</td>
</tr>
+
+<!-- Project -->
<tr <?php echo helper_alternate_class() ?> valign="top">
<td>
<?php echo lang_get( 'project_name' ) ?>
</td>
<td>
<select name="project_id">
- <option value="0" selected="selected"><?php echo lang_get( 'all_projects' ); ?></option>
- <?php print_project_option_list( ALL_PROJECTS, false ) ?>
+ <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( $t_edit_project_id, false ) ?>
</select>
</td>
</tr>
+
+<!-- Config option name -->
<tr <?php echo helper_alternate_class() ?> valign="top">
<td>
<?php echo lang_get( 'configuration_option' ) ?>
</td>
<td>
- <input type="text" name="config_option" value="" size="64" maxlength="64" />
+ <input type="text" name="config_option"
+ value="<?php echo $t_edit_option; ?>"
+ size="64" maxlength="64" />
</td>
</tr>
+
+<!-- Option type -->
<tr <?php echo helper_alternate_class() ?> valign="top">
<td>
<?php echo lang_get( 'configuration_option_type' ) ?>
</td>
<td>
<select 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>
</td>
</tr>
+
+<!-- Option Value -->
<tr <?php echo helper_alternate_class() ?> valign="top">
<td>
<?php echo lang_get( 'configuration_option_value' ) ?>
</td>
<td>
- <textarea name="value" cols="80" rows="10"></textarea>
+ <textarea name="value" cols="80" rows="10"><?php
+ echo print_config_value_as_string( $t_edit_type, $t_edit_value, false );
+ ?></textarea>
</td>
</tr>
+
<tr>
<td colspan="2">
- <input type="submit" name="config_set" class="button" value="<?php echo lang_get( 'set_configuration_option' ) ?>" />
+ <input type="submit" name="config_set" class="button" value="<?php echo lang_get( 'set_configuration_option' ) ?>" />
</td>
</tr>
+
</table>
</form>
+
<?php
} # end user can change config
?>
</div>
+
<?php
html_page_bottom();
View
212 adm_config_set.php
@@ -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' );
@@ -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 );
@@ -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;
}
View
39 config_defaults_inc.php
@@ -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';
@@ -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';
/**
*
@@ -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',
View
3 core/constant_inc.php
@@ -14,7 +14,7 @@
# You should have received a copy of the GNU General Public License
# along with MantisBT. If not, see <http://www.gnu.org/licenses/>.
-define( 'MANTIS_VERSION', '1.2.13dev' );
+define( 'MANTIS_VERSION', '1.2.13' );
# --- constants -------------------
# magic numbers
@@ -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 );
View
16 core/form_api.php
@@ -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;
}
View
3 core/helper_api.php
@@ -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' ) );
}
/**
View
3 core/obsolete.php
@@ -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' );
View
28 core/print_api.php
@@ -1296,22 +1296,36 @@ 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 ), '">';
- 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
6 doc/CREDITS
@@ -56,8 +56,8 @@ Glenn Henshaw
Julian Fitzell
John Reese
David Hicks
-Jeroen Latour
Damien Regad
+Jeroen Latour
Siebrand Mazeland
Robert Munteanu
Gianluca Sforna
@@ -84,10 +84,10 @@ jotel
robertjf
Frank Rodgers
Chris Fitch
+Johan Guilbaud
davidu
planser
Franck Villaume
-Johan Guilbaud
Véronique Maginot
myplacedk
smhanson
@@ -121,6 +121,7 @@ Jacob Hoover
Jonas Nockert
Markus Schneider
Matt McCutchen
+MatthieuR
Michael Lorer
Michael Weibel
Olivier Mengué
@@ -138,6 +139,7 @@ davethegr8
dwethell
gcompagnon
greenius
+jeckyll
mbaranski
petertc
tfromm
View
44 doc/RELEASE
@@ -1,4 +1,47 @@
MantisBT Release Notes
+======================
+
+1.2.13 Security Release (2012-01-22)
+-------------------------------------------------
+
+MantisBT 1.2.13 is a security update for the stable 1.2.x branch. All
+installations that are currently running any 1.2.x version are strongly advised
+to upgrade to this release.
+
+Two cross site scripting (XSS) vulnerability issues affecting MantisBT 1.2.12
+only (earlier versions are not impacted) were discovered:
+
+ - A malicious person could trick a target user's browser into executing
+ arbitrary JavaScript code (CVE-2013-0197). This vulnerability is
+ critical, due to the affected page (search.php) being usable anonymously
+ on public-facing installations (i.e. without the need for a user login).
+ Refer to issue #15373 for detailed information.
+
+ - A user holding manager/administrator permissions could create a
+ category or project name containing JavaScript code; from that point on,
+ visitors to the Summary page (summary.php) are exposed to having the
+ JavaScript execute within their browser environment. The severity of this
+ issue is mitigated by the need to have a privileged account to modify
+ category and project names.
+ Refer to issue #15384 for detailed information.
+
+A workflow-related security issue was also fixed:
+
+ - A user with "Reporter" permissions can modify the workflow status of any
+ issue to "New" even if they do not have the necessary privileges to make
+ this change.
+ Refer to issue #15258 for detailed information.
+
+In addition to the corrections for the above-mentioned security issues, this
+release also includes several bug fixes and enhancements:
+
+ - improved Manage Configuration page (better performance, ability to filter
+ and edit config options)
+ - support for the built-in SOAP extension in addition to nusoap
+ - updated translations in many languages
+
+A full changelog for the 1.2.x series can be found on the official site. [1]
+
1.2.12 Maintenance Release (2012-11-10)
-------------------------------------------------
@@ -259,6 +302,7 @@ There have also been many improvements to the codebase beyond adding features:
[1] The changelog is split between multiple releases:
+ 1.2.13 http://www.mantisbt.org/bugs/changelog_page.php?version_id=180
1.2.12 http://www.mantisbt.org/bugs/changelog_page.php?version_id=150
1.2.11 http://www.mantisbt.org/bugs/changelog_page.php?version_id=148
1.2.10 http://www.mantisbt.org/bugs/changelog_page.php?version_id=146
View
86 docbook/administration_guide/en/configuration.sgml
@@ -2515,51 +2515,87 @@
<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>
+ <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
- setting.
- </para>
+ <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>
</varlistentry>
<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>
+ <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
@@ -748,6 +748,7 @@ $s_create_user_button = 'Create User';
# manage_page.php
$s_hide_disabled = 'Hide Disabled';
$s_filter_button = 'Apply Filter';
+$s_default_filter = 'Default Filter';
$s_create_filter_link = 'Create Permalink'; # Permalink = Permanent Link
$s_create_short_link = 'Create Short Link';
$s_filter_permalink = 'Following is a permanent link to the currently configured filter:';
@@ -1433,7 +1434,7 @@ $s_delete_attachments = 'Delete attachments';
$s_delete_attachment_button = 'Delete';
$s_delete_attachment_sure_msg = 'Are you sure you wish to delete this attachment?';
$s_upload_issue_attachments = 'Upload issue attachments';
-$s_filters = 'filters';
+$s_filters = 'Filters';
$s_save_filters = 'Save filters';
$s_save_filters_as_shared = 'Save filters as shared';
$s_use_saved_filters = 'Use saved filters';
View
2 manage_user_page.php
@@ -40,7 +40,7 @@
$f_page_number = gpc_get_int( 'page_number', 1 );
$t_user_table = db_get_table( 'mantis_user_table' );
- $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" border="0" alt="' . lang_get( 'protected' ) . '" />';
$c_filter = '';

Showing you all comments on commits in this comparison.

@The-Judge

Are you guys aware of the fact that your download docs ( http://www.mantisbt.org/download.php ) are redirecting to https://sourceforge.net/projects/mantisbt/files/mantis-stable/ with a link named "Download MantisBT 1.2.13", your announcement mail already hit the users inboxes, but the 1.2.13 - sourceforge - folder is still empty?
Noone who doesn't know of Git and this Repo will be able to do the update currently.

@atrol
Mantis Bug Tracker member
atrol commented on d38abf9 Jan 23, 2013

We are aware of it.
There is a discussion to recall the version.
That's why the files are removed at the moment.

Something went wrong with that request. Please try again.