some updater changes as well as tiny bugfixes

git-svn-id: file:///svn/phpbb/trunk@6714 89ea8834-ac86-4346-8a33-228a782c2dd0
This commit is contained in:
Meik Sievertsen 2006-12-06 15:47:50 +00:00
parent cb505ce4b0
commit daeef71d81
8 changed files with 210 additions and 85 deletions

View file

@ -128,7 +128,7 @@ function popup(url, width, height)
<dd class="full" style="text-align: left;"><strong>{files.FILENAME}</strong></dd> <dd class="full" style="text-align: left;"><strong>{files.FILENAME}</strong></dd>
<!-- ELSE --> <!-- ELSE -->
<dt style="width: 60%;"> <dt style="width: 60%;">
<strong>{files.FILENAME}</strong> <strong><!-- IF files.DIR_PART -->{files.DIR_PART}<br /><!-- ENDIF -->{files.FILE_PART}</strong>
<!-- IF files.S_CUSTOM --> <!-- IF files.S_CUSTOM -->
<br /><span><em>{L_FILE_USED}: </em>{files.CUSTOM_ORIGINAL}</span> <br /><span><em>{L_FILE_USED}: </em>{files.CUSTOM_ORIGINAL}</span>
<!-- ENDIF --> <!-- ENDIF -->
@ -141,10 +141,33 @@ function popup(url, width, height)
<dd style="margin-left: 60%;"><input type="checkbox" name="no_update[]" value="{files.FILENAME}" class="radio" />&nbsp; {L_DO_NOT_UPDATE}</dd> <dd style="margin-left: 60%;"><input type="checkbox" name="no_update[]" value="{files.FILENAME}" class="radio" />&nbsp; {L_DO_NOT_UPDATE}</dd>
<!-- ENDIF --> <!-- ENDIF -->
<!-- IF files.STATUS eq 'conflict' --> <!-- IF files.STATUS eq 'conflict' -->
<dd style="margin-left: 60%;"><input type="radio" class="radio" name="conflict[{files.FILENAME}]" value="1" checked="checked" />&nbsp; {L_MERGE_NEW_FILE_OPTION}<br /><input type="radio" class="radio" name="conflict[{files.FILENAME}]" value="2" />&nbsp; {L_MERGE_MOD_FILE_OPTION}</dd> </dl>
<dl>
<dt style="width: 60%"><input type="radio" class="radio" name="conflict[{files.FILENAME}]" value="1" checked="checked" />&nbsp; {L_MERGE_NO_MERGE_NEW_OPTION}</dt>
<dd style="margin-left: 60%;">[<a href="{files.U_VIEW_NO_MERGE_NEW}" onclick="popup('{files.U_VIEW_NO_MERGE_NEW}', 700, 500); return false;">{L_SHOW_DIFF_FINAL}</a>]</dd>
</dl>
<dl>
<dt style="width: 60%"><input type="radio" class="radio" name="conflict[{files.FILENAME}]" value="2" />&nbsp; {L_MERGE_NO_MERGE_MOD_OPTION}</dt>
<dd style="margin-left: 60%;">[<a href="{files.U_VIEW_NO_MERGE_MOD}" onclick="popup('{files.U_VIEW_NO_MERGE_MOD}', 700, 500); return false;">{L_SHOW_DIFF_FINAL}</a>]</dd>
</dl>
<dl>
<dt style="width: 60%"><input type="radio" class="radio" name="conflict[{files.FILENAME}]" value="3" />&nbsp; {L_MERGE_NEW_FILE_OPTION}</dt>
<dd style="margin-left: 60%;">[<a href="{files.U_VIEW_NEW_FILE}" onclick="popup('{files.U_VIEW_NEW_FILE}', 700, 500); return false;">{L_SHOW_DIFF_FINAL}</a>]</dd>
</dl>
<dl>
<dt style="width: 60%"><input type="radio" class="radio" name="conflict[{files.FILENAME}]" value="4" />&nbsp; {L_MERGE_MOD_FILE_OPTION}</dt>
<dd style="margin-left: 60%;">[<a href="{files.U_VIEW_MOD_FILE}" onclick="popup('{files.U_VIEW_MOD_FILE}', 700, 500); return false;">{L_SHOW_DIFF_FINAL}</a>]</dd>
</dl>
<!-- IF not files.S_LAST_ROW -->
</fieldset>
<fieldset>
<legend><img src="{T_IMAGE_PATH}file_{files.STATUS}.gif" alt="{files.L_STATUS}" /></legend>
<!-- ENDIF -->
<!-- ENDIF --> <!-- ENDIF -->
<!-- ENDIF --> <!-- ENDIF -->
</dl> <!-- IF files.STATUS neq 'conflict' --></dl><!-- ENDIF -->
<!-- ENDIF --> <!-- ENDIF -->
<!-- END files --> <!-- END files -->

View file

@ -171,7 +171,7 @@ table.hrdiff caption span {
<!-- IF S_DIFF_CONFLICT_FILE --> <!-- IF S_DIFF_CONFLICT_FILE -->
<div style="float: right;"><strong>{L_NUM_CONFLICTS}: {NUM_CONFLICTS}</strong></div> <div style="float: right;"><strong>{L_NUM_CONFLICTS}: {NUM_CONFLICTS}</strong></div>
<!-- ENDIF --> <!-- ENDIF -->
{DIFF_CONTENT} {DIFF_CONTENT}
</div> </div>
</div> </div>
<span class="corners-bottom"><span></span></span> <span class="corners-bottom"><span></span></span>

View file

@ -308,6 +308,29 @@ class acp_icons
{ {
$order = 0; $order = 0;
if (!($pak_ary = @file($phpbb_root_path . $img_path . '/' . $pak)))
{
trigger_error($user->lang['PAK_FILE_NOT_READABLE'] . adm_back_link($this->u_action), E_USER_WARNING);
}
// Make sure the pak_ary is valid
foreach ($pak_ary as $pak_entry)
{
if (preg_match_all("#'(.*?)', #", $pak_entry, $data))
{
if ((sizeof($data[1]) != 4 && $mode == 'icons') ||
(sizeof($data[1]) != 6 && $mode == 'smilies'))
{
trigger_error($user->lang['WRONG_PAK_TYPE'] . adm_back_link($this->u_action), E_USER_WARNING);
}
}
else
{
trigger_error($user->lang['WRONG_PAK_TYPE'] . adm_back_link($this->u_action), E_USER_WARNING);
}
}
// The user has already selected a smilies_pak file // The user has already selected a smilies_pak file
if ($current == 'delete') if ($current == 'delete')
{ {
@ -343,11 +366,6 @@ class acp_icons
$db->sql_freeresult($result); $db->sql_freeresult($result);
} }
if (!($pak_ary = @file($phpbb_root_path . $img_path . '/' . $pak)))
{
trigger_error($user->lang['PAK_FILE_NOT_READABLE'] . adm_back_link($this->u_action), E_USER_WARNING);
}
foreach ($pak_ary as $pak_entry) foreach ($pak_ary as $pak_entry)
{ {
$data = array(); $data = array();

View file

@ -29,15 +29,22 @@ class acp_php_info
$this->page_title = 'ACP_PHP_INFO'; $this->page_title = 'ACP_PHP_INFO';
ob_start(); ob_start();
phpinfo(INFO_GENERAL | INFO_CONFIGURATION | INFO_MODULES | INFO_VARIABLES); @phpinfo(INFO_GENERAL | INFO_CONFIGURATION | INFO_MODULES | INFO_VARIABLES);
$phpinfo = ob_get_contents(); $phpinfo = ob_get_contents();
ob_end_clean(); ob_end_clean();
$phpinfo = trim($phpinfo);
// Here we play around a little with the PHP Info HTML to try and stylise // Here we play around a little with the PHP Info HTML to try and stylise
// it along phpBB's lines ... hopefully without breaking anything. The idea // it along phpBB's lines ... hopefully without breaking anything. The idea
// for this was nabbed from the PHP annotated manual // for this was nabbed from the PHP annotated manual
preg_match_all('#<body[^>]*>(.*)</body>#si', $phpinfo, $output); preg_match_all('#<body[^>]*>(.*)</body>#si', $phpinfo, $output);
if (empty($phpinfo) || empty($output))
{
trigger_error('NO_PHPINFO_AVAILABLE', E_USER_WARNING);
}
$output = $output[1][0]; $output = $output[1][0];
$output = preg_replace('#<tr class="v"><td>(.*?<a[^>]*><img[^>]*></a>)(.*?)</td></tr>#s', '<tr class="row1"><td><table class="type2"><tr><td>\2</td><td>\1</td></tr></table></td></tr>', $output); $output = preg_replace('#<tr class="v"><td>(.*?<a[^>]*><img[^>]*></a>)(.*?)</td></tr>#s', '<tr class="row1"><td><table class="type2"><tr><td>\2</td><td>\1</td></tr></table></td></tr>', $output);
$output = preg_replace('#<table[^>]+>#i', '<table>', $output); $output = preg_replace('#<table[^>]+>#i', '<table>', $output);

View file

@ -418,7 +418,7 @@ class bbcode_firstpass extends bbcode
$conf = array('highlight.bg', 'highlight.comment', 'highlight.default', 'highlight.html', 'highlight.keyword', 'highlight.string'); $conf = array('highlight.bg', 'highlight.comment', 'highlight.default', 'highlight.html', 'highlight.keyword', 'highlight.string');
foreach ($conf as $ini_var) foreach ($conf as $ini_var)
{ {
ini_set($ini_var, str_replace('highlight.', 'syntax', $ini_var)); @ini_set($ini_var, str_replace('highlight.', 'syntax', $ini_var));
} }
// Because highlight_string is specialcharing the text (but we already did this before), we have to reverse this in order to get correct results // Because highlight_string is specialcharing the text (but we already did this before), we have to reverse this in order to get correct results

View file

@ -88,6 +88,12 @@ class install_update extends module
require($phpbb_root_path . 'includes/db/' . $dbms . '.' . $phpEx); require($phpbb_root_path . 'includes/db/' . $dbms . '.' . $phpEx);
require($phpbb_root_path . 'includes/constants.' . $phpEx); require($phpbb_root_path . 'includes/constants.' . $phpEx);
// Special options for conflicts
define('MERGE_NO_MERGE_NEW', 1);
define('MERGE_NO_MERGE_MOD', 2);
define('MERGE_NEW_FILE', 3);
define('MERGE_MOD_FILE', 4);
$db = new $sql_db(); $db = new $sql_db();
// Connect to DB // Connect to DB
@ -113,11 +119,6 @@ class install_update extends module
$auth->acl($user->data); $auth->acl($user->data);
$user->setup('install'); $user->setup('install');
// Include renderer and engine
$this->include_file('includes/diff/diff.' . $phpEx);
$this->include_file('includes/diff/engine.' . $phpEx);
$this->include_file('includes/diff/renderer.' . $phpEx);
// If we are within the intro page we need to make sure we get up-to-date version info // If we are within the intro page we need to make sure we get up-to-date version info
if ($sub == 'intro') if ($sub == 'intro')
{ {
@ -192,6 +193,11 @@ class install_update extends module
$user->lang = array_merge($user->lang, $lang); $user->lang = array_merge($user->lang, $lang);
} }
// Include renderer and engine
$this->include_file('includes/diff/diff.' . $phpEx);
$this->include_file('includes/diff/engine.' . $phpEx);
$this->include_file('includes/diff/renderer.' . $phpEx);
// Make sure we stay at the file check if checking the files again // Make sure we stay at the file check if checking the files again
if (!empty($_POST['check_again'])) if (!empty($_POST['check_again']))
{ {
@ -294,18 +300,38 @@ class install_update extends module
foreach ($filelist as $file_struct) foreach ($filelist as $file_struct)
{ {
$filename = htmlspecialchars($file_struct['filename']);
if (strrpos($filename, '/') !== false)
{
$dir_part = substr($filename, 0, strrpos($filename, '/') + 1);
$file_part = substr($filename, strrpos($filename, '/') + 1);
}
else
{
$dir_part = '';
$file_part = $filename;
}
$diff_url = append_sid($this->p_master->module_url, "mode=$mode&amp;sub=file_check&amp;action=diff&amp;status=$status&amp;file=" . urlencode($file_struct['filename']));
$template->assign_block_vars('files', array( $template->assign_block_vars('files', array(
'STATUS' => $status, 'STATUS' => $status,
'FILENAME' => htmlspecialchars($file_struct['filename']), 'FILENAME' => $filename,
'DIR_PART' => $dir_part,
'FILE_PART' => $file_part,
'NUM_CONFLICTS' => (isset($file_struct['conflicts'])) ? $file_struct['conflicts'] : 0, 'NUM_CONFLICTS' => (isset($file_struct['conflicts'])) ? $file_struct['conflicts'] : 0,
'S_CUSTOM' => ($file_struct['custom']) ? true : false, 'S_CUSTOM' => ($file_struct['custom']) ? true : false,
'CUSTOM_ORIGINAL' => ($file_struct['custom']) ? $file_struct['original'] : '', 'CUSTOM_ORIGINAL' => ($file_struct['custom']) ? $file_struct['original'] : '',
'U_SHOW_DIFF' => append_sid($this->p_master->module_url, "mode=$mode&amp;sub=file_check&amp;action=diff&amp;status=$status&amp;file=" . urlencode($file_struct['filename'])), 'U_SHOW_DIFF' => $diff_url,
'UA_SHOW_DIFF' => append_sid($this->p_master->module_url, "mode=$mode&sub=file_check&action=diff&status=$status&file=" . urlencode($file_struct['filename']), false),
'L_SHOW_DIFF' => ($status != 'up_to_date') ? $user->lang['SHOW_DIFF_' . strtoupper($status)] : '', 'L_SHOW_DIFF' => ($status != 'up_to_date') ? $user->lang['SHOW_DIFF_' . strtoupper($status)] : '',
'U_VIEW_MOD_FILE' => $diff_url . '&amp;op=' . MERGE_MOD_FILE,
'U_VIEW_NEW_FILE' => $diff_url . '&amp;op=' . MERGE_NEW_FILE,
'U_VIEW_NO_MERGE_MOD' => $diff_url . '&amp;op=' . MERGE_NO_MERGE_MOD,
'U_VIEW_NO_MERGE_NEW' => $diff_url . '&amp;op=' . MERGE_NO_MERGE_NEW,
)); ));
} }
} }
@ -586,16 +612,10 @@ class install_update extends module
case 'modified': case 'modified':
$tmp = array( $diff = $this->return_diff($this->old_location . $original_filename, $phpbb_root_path . $file_struct['filename'], $this->new_location . $original_filename);
'file1' => file_get_contents($this->old_location . $original_filename),
'file2' => file_get_contents($phpbb_root_path . $file_struct['filename']),
'file3' => file_get_contents($this->new_location . $original_filename),
);
$diff = &new diff3($tmp['file1'], $tmp['file2'], $tmp['file3']);
$contents = implode("\n", $diff->merged_output()); $contents = implode("\n", $diff->merged_output());
unset($diff);
unset($tmp, $diff);
if ($update_mode == 'download') if ($update_mode == 'download')
{ {
@ -611,32 +631,41 @@ class install_update extends module
case 'conflict': case 'conflict':
$tmp = array( $option = $conflicts[$file_struct['filename']];
'file1' => file_get_contents($this->old_location . $original_filename), $contents = '';
'file2' => file_get_contents($phpbb_root_path . $file_struct['filename']),
'file3' => file_get_contents($this->new_location . $original_filename),
);
$diff = &new diff3($tmp['file1'], $tmp['file2'], $tmp['file3']); switch ($option)
{
case MERGE_NO_MERGE_NEW:
$contents = file_get_contents($this->new_location . $original_filename);
break;
unset($tmp); case MERGE_NO_MERGE_MOD:
$contents = file_get_contents($phpbb_root_path . $file_struct['filename']);
break;
if ($conflicts[$file_struct['filename']] == 1) default:
{
$contents = implode("\n", $diff->merged_new_output()); $diff = $this->return_diff($this->old_location . $original_filename, $phpbb_root_path . $file_struct['filename'], $this->new_location . $original_filename);
}
else if ($conflicts[$file_struct['filename']] == 2) if ($option == MERGE_NEW_FILE)
{ {
$contents = implode("\n", $diff->merged_orig_output()); $contents = implode("\n", $diff->merged_new_output());
} }
else else if ($option == MERGE_MOD_FILE)
{ {
unset($diff); $contents = implode("\n", $diff->merged_orig_output());
}
else
{
unset($diff);
break 2;
}
unset($diff);
break; break;
} }
unset($diff);
if ($update_mode == 'download') if ($update_mode == 'download')
{ {
$compress->add_data($contents, $file_struct['filename']); $compress->add_data($contents, $file_struct['filename']);
@ -751,59 +780,71 @@ class install_update extends module
switch ($status) switch ($status)
{ {
case 'conflict': case 'conflict':
$tmp = array( $option = request_var('op', 0);
'file1' => file_get_contents($this->old_location . $original_file),
'file2' => file_get_contents($phpbb_root_path . $file),
'file3' => file_get_contents($this->new_location . $original_file),
);
$diff = &new diff3($tmp['file1'], $tmp['file2'], $tmp['file3']); switch ($option)
{
case MERGE_NO_MERGE_NEW:
case MERGE_NO_MERGE_MOD:
unset($tmp); $diff = $this->return_diff(array(), ($option == MERGE_NO_MERGE_NEW) ? $this->new_location . $original_file : $phpbb_root_path . $file);
$template->assign_var('S_DIFF_NEW_FILE', true);
$diff_mode = 'inline';
$this->page_title = 'VIEWING_FILE_CONTENTS';
break;
case MERGE_NEW_FILE:
case MERGE_MOD_FILE:
$diff = $this->return_diff($this->old_location . $original_file, $phpbb_root_path . $file, $this->new_location . $original_file);
$tmp = array(
'file1' => array(),
'file2' => ($option == MERGE_NEW_FILE) ? implode("\n", $diff->merged_new_output()) : implode("\n", $diff->merged_orig_output()),
);
$diff = &new diff($tmp['file1'], $tmp['file2']);
unset($tmp);
$template->assign_var('S_DIFF_NEW_FILE', true);
$diff_mode = 'inline';
$this->page_title = 'VIEWING_FILE_CONTENTS';
break;
default:
$diff = $this->return_diff($this->old_location . $original_file, $phpbb_root_path . $file, $this->new_location . $original_file);
$template->assign_vars(array(
'S_DIFF_CONFLICT_FILE' => true,
'NUM_CONFLICTS' => $diff->merged_output(false, false, false, true))
);
break;
}
$template->assign_vars(array(
'S_DIFF_CONFLICT_FILE' => true,
'NUM_CONFLICTS' => $diff->merged_output(false, false, false, true))
);
break; break;
case 'modified': case 'modified':
$tmp = array( $diff = $this->return_diff($this->old_location . $original_file, $phpbb_root_path . $original_file, $this->new_location . $file);
'file1' => file_get_contents($this->old_location . $original_file),
'file2' => file_get_contents($phpbb_root_path . $original_file),
'file3' => file_get_contents($this->new_location . $file),
);
$diff = &new diff3($tmp['file1'], $tmp['file2'], $tmp['file3']);
unset($tmp);
break; break;
case 'not_modified': case 'not_modified':
case 'new_conflict': case 'new_conflict':
$tmp = array( $diff = $this->return_diff($phpbb_root_path . $file, $this->new_location . $original_file);
'file1' => file_get_contents($phpbb_root_path . $file),
'file2' => file_get_contents($this->new_location . $original_file),
);
$diff = &new diff($tmp['file1'], $tmp['file2']);
unset($tmp);
break; break;
case 'new': case 'new':
$tmp = array(
'file1' => array(),
'file2' => file_get_contents($this->new_location . $original_file),
);
$diff = &new diff($tmp['file1'], $tmp['file2']); $diff = $this->return_diff(array(), $this->new_location . $original_file);
unset($tmp);
$template->assign_var('S_DIFF_NEW_FILE', true); $template->assign_var('S_DIFF_NEW_FILE', true);
$diff_mode = 'inline'; $diff_mode = 'inline';
$this->page_title = 'VIEWING_FILE_CONTENTS'; $this->page_title = 'VIEWING_FILE_CONTENTS';
break; break;
} }
@ -1154,7 +1195,7 @@ class install_update extends module
*/ */
function include_file($filename) function include_file($filename)
{ {
global $phpbb_root_path; global $phpbb_root_path, $phpEx;
if (!empty($this->update_info['files']) && in_array($filename, $this->update_info['files'])) if (!empty($this->update_info['files']) && in_array($filename, $this->update_info['files']))
{ {
@ -1165,6 +1206,37 @@ class install_update extends module
include_once($phpbb_root_path . $filename); include_once($phpbb_root_path . $filename);
} }
} }
/**
* Wrapper for returning a diff object
*/
function &return_diff()
{
$args = func_get_args();
$three_way_diff = (func_num_args() > 2) ? true : false;
$file1 = array_shift($args);
$file2 = array_shift($args);
$tmp['file1'] = (!empty($file1) && is_string($file1)) ? file_get_contents($file1) : $file1;
$tmp['file2'] = (!empty($file2) && is_string($file2)) ? file_get_contents($file2) : $file2;
if ($three_way_diff)
{
$file3 = array_shift($args);
$tmp['file3'] = (!empty($file3) && is_string($file3)) ? file_get_contents($file3) : $file3;
$diff = &new diff3($tmp['file1'], $tmp['file2'], $tmp['file3']);
}
else
{
$diff = &new diff($tmp['file1'], $tmp['file2']);
}
unset($tmp);
return $diff;
}
} }
?> ?>

View file

@ -273,6 +273,8 @@ $lang = array_merge($lang, array(
// PHP info // PHP info
$lang = array_merge($lang, array( $lang = array_merge($lang, array(
'ACP_PHP_INFO_EXPLAIN' => 'This page lists information on the version of PHP installed on this server. It includes details of loaded modules, available variables and default settings. This information may be useful when diagnosing problems. Please be aware that some hosting companies will limit what information is displayed here for security reasons. You are advised to not give out any details on this page except when asked by support or other Team Member on the support forums.', 'ACP_PHP_INFO_EXPLAIN' => 'This page lists information on the version of PHP installed on this server. It includes details of loaded modules, available variables and default settings. This information may be useful when diagnosing problems. Please be aware that some hosting companies will limit what information is displayed here for security reasons. You are advised to not give out any details on this page except when asked by support or other Team Member on the support forums.',
'NO_PHPINFO_AVAILABLE' => 'The PHP informations are unable to be determined. Phpinfo() has been disabled for security reasons.',
)); ));
// Logs // Logs

View file

@ -368,8 +368,10 @@ $lang = array_merge($lang, array(
'LOGIN_UPDATE_EXPLAIN' => 'In order to update your installation you need to login first.', 'LOGIN_UPDATE_EXPLAIN' => 'In order to update your installation you need to login first.',
'MAPPING_FILE_STRUCTURE' => 'To ease the upload here are the file locations which map your phpBB installation.', 'MAPPING_FILE_STRUCTURE' => 'To ease the upload here are the file locations which map your phpBB installation.',
'MERGE_MOD_FILE_OPTION' => 'Use modified file code on final merge', 'MERGE_NO_MERGE_NEW_OPTION' => 'Do not merge - use new file',
'MERGE_NEW_FILE_OPTION' => 'Use new file code on final merge', 'MERGE_NO_MERGE_MOD_OPTION' => 'Do not merge - use currently installed file',
'MERGE_MOD_FILE_OPTION' => 'Merge differences and use modified code within conflicting block',
'MERGE_NEW_FILE_OPTION' => 'Merge differences and use new file code within conflicting block',
'MERGE_SELECT_ERROR' => 'Conflicting file merge modes are not correctly selected.', 'MERGE_SELECT_ERROR' => 'Conflicting file merge modes are not correctly selected.',
'NEW_FILE' => 'New updated file', 'NEW_FILE' => 'New updated file',
@ -400,6 +402,7 @@ $lang = array_merge($lang, array(
'SELECT_DOWNLOAD_FORMAT' => 'Select download archive format', 'SELECT_DOWNLOAD_FORMAT' => 'Select download archive format',
'SELECT_FTP_SETTINGS' => 'Select FTP Settings', 'SELECT_FTP_SETTINGS' => 'Select FTP Settings',
'SHOW_DIFF_CONFLICT' => 'Show differences/conflicts', 'SHOW_DIFF_CONFLICT' => 'Show differences/conflicts',
'SHOW_DIFF_FINAL' => 'Show resulting file',
'SHOW_DIFF_MODIFIED' => 'Show merged differences', 'SHOW_DIFF_MODIFIED' => 'Show merged differences',
'SHOW_DIFF_NEW' => 'Show file contents', 'SHOW_DIFF_NEW' => 'Show file contents',
'SHOW_DIFF_NEW_CONFLICT' => 'Show differences', 'SHOW_DIFF_NEW_CONFLICT' => 'Show differences',