Compare commits

...

32 Commits

Author SHA1 Message Date
Denis Flaven
5a7c17b80a (retrofit from trunk) Properly sanitize the "switch_env" parameter and take it into account only if it contains a valid value.
SVN:2.0.3[4242]
2016-06-22 12:18:14 +00:00
Denis Flaven
0682f9f3b1 Use one-way encryption for storing the token used for the "Forgotten password" feature.
SVN:2.0.3[3924]
2016-02-19 18:23:00 +00:00
Denis Flaven
b2b0ab9eff Fixed a potential XSS vulnerability.
SVN:2.0.3[3665]
2015-07-30 09:14:51 +00:00
Denis Flaven
000dd3726d Enhancement: allow the API to create entries with a specified user_login.
SVN:2.0.3[3517]
2015-03-24 17:12:35 +00:00
Denis Flaven
0a8419c1d8 #594: properly display attachments inside "properties" by closing the span and the fieldset in non-edit mode.
SVN:2.0.3[3513]
2015-03-23 17:57:35 +00:00
Romain Quetiez
61fb800f23 #1060 Internal: improved the symptoms when calling MetaModel::GetAttributeDef with an invalid attribute code (feedback on the class name and no more FATAL errors) -retrofit from trunk
SVN:2.0.3[3494]
2015-02-09 13:16:30 +00:00
Romain Quetiez
ea7a8888e9 Partial retrofit of [3438]: fixes the helper RenameEnumValueInDB which did work only IF the values differ in case (ex. IpPhone to IPPhone)
SVN:2.0.3[3488]
2015-01-09 10:01:24 +00:00
Romain Quetiez
1e66220077 #1020 Restrict dashboard/shortcut refresh interval -retrofit from trunk
SVN:2.0.3[3449]
2014-12-03 14:18:16 +00:00
Romain Quetiez
ad3178d02e #1027 Regression introduced in [3148] thus in 2.0.3 (cache the reconciliation for external keys on the CSV import) a cache hit on an ambiguous external key was not correctly handled -retroffited from trunk
SVN:2.0.3[3441]
2014-12-01 19:21:19 +00:00
Romain Quetiez
028d4b52e4 #1007 Unexpected change of the case log when doing massive update of a User Request (+ hide the checkbox for the status because it makes no sense) -retrofit from trunk
SVN:2.0.3[3395]
2014-10-31 15:11:15 +00:00
Denis Flaven
6d4a78db20 #1011 Proper resizing of the dialog box for managing 1:n links - removed a trace - retrofit from trunk.
SVN:2.0.3[3391]
2014-10-30 08:56:15 +00:00
Denis Flaven
457ee5de64 Fixed the support of a non-default port for MySQL, thanks to theBigOne!
SVN:2.0.3[3369]
2014-10-16 14:34:25 +00:00
Denis Flaven
9bec433e94 #968 (continued) do not loose the "advanced mode" flag in the interactive display (retrofit from trunk).
SVN:2.0.3[3358]
2014-10-02 09:40:29 +00:00
Romain Quetiez
895a5fc5b0 #968 Interactive CSV Export truncated or missing characters (since 2.0.3) -reintegrated from trunk
SVN:2.0.3[3355]
2014-09-25 15:23:43 +00:00
Romain Quetiez
4c735a6dff #991 CSV export truncated (system dependent, since 2.0) due to a bug in iconv, the workaround is to do little by little -retrofit from trunk
SVN:2.0.3[3353]
2014-09-25 15:07:01 +00:00
Denis Flaven
5fd193885e #932: fixed a regression introduced by [r3319]... in case a criteria is present several times, retrofit from trunk.
SVN:2.0.3[3345]
2014-09-17 15:42:55 +00:00
Denis Flaven
9277184ba0 Bug fix: FetchAssoc was broken when dealing with in-memory sets, retrofit from trunk.
SVN:2.0.3[3341]
2014-09-16 09:47:03 +00:00
Romain Quetiez
aa0bd6c1ea #778 Issue on list sort order when editing an element - fixed a regression -reintegrated from trunk
SVN:2.0.3[3336]
2014-09-15 16:08:55 +00:00
Romain Quetiez
d8382a541c #778 Issue on list sort order when editing an element -reintegrated from trunk
SVN:2.0.3[3333]
2014-09-15 14:06:34 +00:00
Romain Quetiez
5e3eab90a6 #986 Search form: handle indirect external keys -reintegrated from trunk
SVN:2.0.3[3331]
2014-09-15 14:02:03 +00:00
Romain Quetiez
2c4c6a6690 #987 Usage login prevents from user deletion -reintegrated from trunk
SVN:2.0.3[3329]
2014-09-15 13:57:51 +00:00
Romain Quetiez
5e600c88a7 #932 Search form should be prefilled when running a search "shortcut" - very little progress: fixed the case when several criteria are given -reintegrated from trunk
SVN:2.0.3[3327]
2014-09-15 13:51:36 +00:00
Romain Quetiez
3c4057d546 #985 Shortcut auto refresh degrading table cosmetics -reintegrated from trunk
SVN:2.0.3[3325]
2014-09-15 13:48:28 +00:00
Romain Quetiez
9071f340a8 #984 Dashboard auto refresh degrading table functionalities like sorting -retrofit from trunk
SVN:2.0.3[3323]
2014-09-15 13:44:59 +00:00
Romain Quetiez
5cb9363cce #976 Fixed a typo introduced in the implementation of the fix -reintegrated from trunk
SVN:2.0.3[3316]
2014-09-11 08:26:08 +00:00
Romain Quetiez
9dd4b05866 Improved the processing of background task to enable more advanced functionalities like queuing (protection against reentrance) -reintegrated from trunk
SVN:2.0.3[3314]
2014-09-03 09:56:44 +00:00
Denis Flaven
4841738f01 Protect dashboards against invalid queries in "grouped by" dashlets.
SVN:2.0.3[3309]
2014-08-28 16:00:31 +00:00
Denis Flaven
8632d5597a #976: make sure that we do not bypass the method that computes the reference for newly created tickets.
SVN:2.0.3[3308]
2014-08-28 15:57:21 +00:00
Romain Quetiez
01e4f6af79 Improved the processing of background task to enable more advanced functionalities like queuing (factorized the error handling) -reintegrated from trunk
SVN:2.0.3[3303]
2014-08-21 08:55:57 +00:00
Romain Quetiez
0a2cdff528 Improved the processing of background task to enable more advanced functionalities like queuing -reintegrated from trunk
SVN:2.0.3[3301]
2014-08-19 10:10:18 +00:00
Romain Quetiez
b5166938df Legacy user rights management: allow the deletion of a profile in one step (it was nearly impossible because of the numerous related records, mainly of type URP_ActionGrant, for which iTop was requesting a manual deletion) -retrofit from trunk
SVN:2.0.3[3279]
2014-07-16 10:54:19 +00:00
Romain Quetiez
441b059581 Releasing 2.0.3
SVN:2.0.3[3276]
2014-07-15 13:36:53 +00:00
34 changed files with 481 additions and 190 deletions

View File

@@ -941,12 +941,12 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
return $oDataTable->Display($oPage, $oSettings, $bDisplayMenu, $sSelectMode, $bViewLink, $aExtraParams);
}
static function DisplaySetAsCSV(WebPage $oPage, CMDBObjectSet $oSet, $aParams = array())
static function DisplaySetAsCSV(WebPage $oPage, CMDBObjectSet $oSet, $aParams = array(), $sCharset = 'UTF-8')
{
$oPage->add(self::GetSetAsCSV($oSet, $aParams));
$oPage->add(self::GetSetAsCSV($oSet, $aParams, $sCharset));
}
static function GetSetAsCSV(DBObjectSet $oSet, $aParams = array())
static function GetSetAsCSV(DBObjectSet $oSet, $aParams = array(), $sCharset = 'UTF-8')
{
$sSeparator = isset($aParams['separator']) ? $aParams['separator'] : ','; // default separator is comma
$sTextQualifier = isset($aParams['text_qualifier']) ? $aParams['text_qualifier'] : '"'; // default text qualifier is double quote
@@ -1064,7 +1064,8 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
else
{
$value = $oObj->Get($sAttCodeEx);
$aRow[] = $oAttDef->GetAsCSV($value, $sSeparator, $sTextQualifier, $oObj, $bLocalize);
$sCSVValue = $oAttDef->GetAsCSV($value, $sSeparator, $sTextQualifier, $oObj, $bLocalize);
$aRow[] = iconv('UTF-8', $sCharset.'//IGNORE//TRANSLIT', $sCSVValue);
}
}
}
@@ -1436,20 +1437,24 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
}
$oAttDef = MetaModel::GetAttributeDef($sClassName, $sFilterCode);
if ($oAttDef->IsExternalKey())
if ($oAttDef->IsExternalKey(EXTKEY_ABSOLUTE))
{
$sTargetClass = $oAttDef->GetTargetClass();
$oKeyAttDef = $oAttDef->GetFinalAttDef();
$sKeyAttClass = $oKeyAttDef->GetHostClass();
$sKeyAttCode = $oKeyAttDef->GetCode();
$sTargetClass = $oKeyAttDef->GetTargetClass();
$oSearch = new DBObjectSearch($sTargetClass);
$oSearch->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', true);
$oAllowedValues = new DBObjectSet($oSearch);
$iFieldSize = $oAttDef->GetMaxSize();
$iMaxComboLength = $oAttDef->GetMaximumComboLength();
$sHtml .= "<label>".MetaModel::GetFilterLabel($sClassName, $sFilterCode).":</label>&nbsp;";
$iFieldSize = $oKeyAttDef->GetMaxSize();
$iMaxComboLength = $oKeyAttDef->GetMaximumComboLength();
$sHtml .= "<label>".MetaModel::GetFilterLabel($sKeyAttClass, $sKeyAttCode).":</label>&nbsp;";
$aExtKeyParams = $aExtraParams;
$aExtKeyParams['iFieldSize'] = $oAttDef->GetMaxSize();
$aExtKeyParams['iMinChars'] = $oAttDef->GetMinAutoCompleteChars();
$sHtml .= UIExtKeyWidget::DisplayFromAttCode($oPage, $sFilterCode, $sClassName, $oAttDef->GetLabel(), $oAllowedValues, $sFilterValue, $sSearchFormId.'search_'.$sFilterCode, false, $sFilterCode, '', $aExtKeyParams, true);
$aExtKeyParams['iFieldSize'] = $oKeyAttDef->GetMaxSize();
$aExtKeyParams['iMinChars'] = $oKeyAttDef->GetMinAutoCompleteChars();
$sHtml .= UIExtKeyWidget::DisplayFromAttCode($oPage, $sKeyAttCode, $sKeyAttClass, $oAttDef->GetLabel(), $oAllowedValues, $sFilterValue, $sSearchFormId.'search_'.$sFilterCode, false, $sFilterCode, '', $aExtKeyParams, true);
}
else
{
@@ -3074,7 +3079,7 @@ EOF
// Attribute is read-only
$sHTMLValue = $this->GetAsHTML($sAttCode);
$sHTMLValue .= '<input type="hidden" id="'.$sInputId.'" name="attr_'.$sPrefix.$sAttCode.'" value="'.htmlentities($this->Get($sAttCode), ENT_QUOTES, 'UTF-8').'"/>';
$sHTMLValue .= '<input type="hidden" id="'.$sInputId.'" name="attr_'.$sPrefix.$sAttCode.'" value="'.htmlentities($this->GetEditValue($sAttCode), ENT_QUOTES, 'UTF-8').'"/>';
$aFieldsMap[$sAttCode] = $sInputId;
$sComment .= $sSynchroIcon;
}
@@ -3251,7 +3256,11 @@ EOF
$currValue = $aKeys[0]; // The only value is the first key
//echo "<p>current value for $sAttCode : $currValue</p>";
$oDummyObj->Set($sAttCode, $currValue);
$aComments[$sAttCode] = '<input type="checkbox" checked id="enable_'.$iFormId.'_'.$sAttCode.'" onClick="ToogleField(this.checked, \''.$iFormId.'_'.$sAttCode.'\')"/>';
$aComments[$sAttCode] = '';
if ($sAttCode != MetaModel::GetStateAttributeCode($sClass))
{
$aComments[$sAttCode] .= '<input type="checkbox" checked id="enable_'.$iFormId.'_'.$sAttCode.'" onClick="ToogleField(this.checked, \''.$iFormId.'_'.$sAttCode.'\')"/>';
}
$aComments[$sAttCode] .= '<div class="mono_value">1</div>';
}
else
@@ -3278,7 +3287,11 @@ EOF
$sReadyScript .= "$('#multi_values_$sAttCode').qtip( { content: '$sTip', show: 'mouseover', hide: 'mouseout', style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );";
$oDummyObj->Set($sAttCode, null);
$aComments[$sAttCode] = '<input type="checkbox" id="enable_'.$iFormId.'_'.$sAttCode.'" onClick="ToogleField(this.checked, \''.$iFormId.'_'.$sAttCode.'\')"/>';
$aComments[$sAttCode] = '';
if ($sAttCode != MetaModel::GetStateAttributeCode($sClass))
{
$aComments[$sAttCode] .= '<input type="checkbox" id="enable_'.$iFormId.'_'.$sAttCode.'" onClick="ToogleField(this.checked, \''.$iFormId.'_'.$sAttCode.'\')"/>';
}
$aComments[$sAttCode] .= '<div class="multi_values" id="multi_values_'.$sAttCode.'">'.$iCount.'</div>';
}
$sReadyScript .= 'ToogleField('.(($iCount == 1) ? 'true': 'false').', \''.$iFormId.'_'.$sAttCode.'\');'."\n";

View File

@@ -91,7 +91,7 @@ abstract class Dashboard
}
if ($oAutoReloadInterval = $oAutoReloadNode->getElementsByTagName('interval')->item(0))
{
$this->iAutoReloadSec = max(5, (int)$oAutoReloadInterval->textContent);
$this->iAutoReloadSec = max(MetaModel::GetConfig()->Get('min_reload_interval'), (int)$oAutoReloadInterval->textContent);
}
}
@@ -235,7 +235,7 @@ abstract class Dashboard
$this->sLayoutClass = $aParams['layout_class'];
$this->sTitle = $aParams['title'];
$this->bAutoReload = $aParams['auto_reload'] == 'true';
$this->iAutoReloadSec = max(5, (int) $aParams['auto_reload_sec']);
$this->iAutoReloadSec = max(MetaModel::GetConfig()->Get('min_reload_interval'), (int) $aParams['auto_reload_sec']);
foreach($aParams['cells'] as $aCell)
{
@@ -300,7 +300,7 @@ abstract class Dashboard
public function SetAutoReloadInterval($iAutoReloadSec)
{
$this->iAutoReloadSec = max(5, (int)$iAutoReloadSec);
$this->iAutoReloadSec = max(MetaModel::GetConfig()->Get('min_reload_interval'), (int)$iAutoReloadSec);
}
public function AddDashlet($oDashlet)
@@ -312,7 +312,7 @@ abstract class Dashboard
public function Render($oPage, $bEditMode = false, $aExtraParams = array())
{
$oPage->add('<h1>'.Dict::S($this->sTitle).'</h1>');
$oPage->add('<h1>'.htmlentities(Dict::S($this->sTitle), ENT_QUOTES, 'UTF-8', false).'</h1>');
$oLayout = new $this->sLayoutClass;
$oLayout->Render($oPage, $this->aCells, $bEditMode, $aExtraParams);
if (!$bEditMode)
@@ -357,16 +357,17 @@ abstract class Dashboard
$oField = new DesignerBooleanField('auto_reload', Dict::S('UI:DashboardEdit:AutoReload'), $this->bAutoReload);
$oForm->AddField($oField);
$oField = new DesignerTextField('auto_reload_sec', Dict::S('UI:DashboardEdit:AutoReloadSec'), $this->iAutoReloadSec);
$oField->SetValidationPattern('^$|^0*([5-9]|[1-9][0-9]+)$'); // Can be empty, or a number > 4
$oField = new DesignerIntegerField('auto_reload_sec', Dict::S('UI:DashboardEdit:AutoReloadSec'), $this->iAutoReloadSec);
$oField->SetBoundaries(MetaModel::GetConfig()->Get('min_reload_interval'), null); // no upper limit
$oForm->AddField($oField);
$this->SetFormParams($oForm);
$oForm->RenderAsPropertySheet($oPage, false, '.itop-dashboard');
$oPage->add('</div>');
$sRateTitle = addslashes(Dict::S('UI:DashboardEdit:AutoReloadSec+'));
$sRateTitle = addslashes(Dict::Format('UI:DashboardEdit:AutoReloadSec+', MetaModel::GetConfig()->Get('min_reload_interval')));
$oPage->add_ready_script(
<<<EOF
// Note: the title gets deleted by the validation mechanism

View File

@@ -506,11 +506,19 @@ abstract class DashletGroupBy extends Dashlet
$sGroupBy = $this->aProperties['group_by'];
$sStyle = $this->aProperties['style'];
// First perform the query - if the OQL is not ok, it will generate an exception : no need to go further
$oQuery = $this->oModelReflection->GetQuery($sQuery);
$sClass = $oQuery->GetClass();
$sClassAlias = $oQuery->GetClassAlias();
// First perform the query - if the OQL is not ok, it will generate an exception : no need to go further
try
{
$oQuery = $this->oModelReflection->GetQuery($sQuery);
$sClass = $oQuery->GetClass();
$sClassAlias = $oQuery->GetClassAlias();
}
catch(Exception $e)
{
// Invalid query, let the user edit the dashlet/dashboard anyhow
$sClass = '';
$sClassAlias = '';
}
// Check groupby... it can be wrong at this stage
if (preg_match('/^(.*):(.*)$/', $sGroupBy, $aMatches))
{

View File

@@ -551,6 +551,7 @@ EOF;
$oPage->add_ready_script(
<<<EOF
var oTable = $('#{$this->iListId} table.listResults');
oTable.tableHover();
oTable.tablesorter( { $sHeaders widgets: ['myZebra', 'truncatedList']} ).tablesorterPager({container: $('#pager{$this->iListId}'), totalRows:$iCount, size: $iPageSize, filter: '$sOQL', extra_params: '$sExtraParams', select_mode: '$sSelectModeJS', displayKey: $sDisplayKey, columns: $sJSColumns, class_aliases: $sJSClassAliases $sCssCount});
EOF
);

View File

@@ -226,7 +226,7 @@ class DisplayBlock
if (is_numeric($aExtraParams['auto_reload']) && ($aExtraParams['auto_reload'] > 0))
{
$bAutoReload = true;
$iReloadInterval = max(5, $aExtraParams['auto_reload'])*1000;
$iReloadInterval = max(MetaModel::GetConfig()->Get('min_reload_interval'), $aExtraParams['auto_reload'])*1000;
}
else
{
@@ -783,7 +783,8 @@ class DisplayBlock
$sLinkToToggle = $sLinkToToggle.'&advanced=1';
$sChecked = '';
}
$sAjaxLink = $sDownloadLink.'&charset=UTF-8'; // Includes &fields_advanced=1 if in advanced mode
/*
$sCSVData = cmdbAbstractObject::GetSetAsCSV($this->m_oSet, array('fields_advanced' => $bAdvancedMode));
$sCharset = MetaModel::GetConfig()->Get('csv_file_default_charset');
@@ -833,7 +834,7 @@ class DisplayBlock
$sHtml .= "<div id=\"csv_content_loading\"><div style=\"width: 250px; height: 20px; background: url(../setup/orange-progress.gif); border: 1px #999 solid; margin-left:auto; margin-right: auto; text-align: center;\">".Dict::S('UI:Loading')."</div></div><textarea id=\"csv_content\" style=\"display:none;\">\n";
//$sHtml .= htmlentities($sCSVData, ENT_QUOTES, 'UTF-8');
$sHtml .= "</textarea>\n";
$oPage->add_ready_script("$.post('$sDownloadLink', {}, function(data) { $('#csv_content').html(data); $('#csv_content_loading').hide(); $('#csv_content').show();} );");
$oPage->add_ready_script("$.post('$sAjaxLink', {}, function(data) { $('#csv_content').html(data); $('#csv_content_loading').hide(); $('#csv_content').show();} );");
break;
case 'modify':

View File

@@ -814,6 +814,74 @@ EOF
}
}
class DesignerIntegerField extends DesignerFormField
{
protected $iMin; // Lower boundary, inclusive
protected $iMax; // Higher boundary, inclusive
public function __construct($sCode, $sLabel = '', $defaultValue = '')
{
parent::__construct($sCode, $sLabel, $defaultValue);
$this->iMin = 0; // Positive integer is the default
$this->iMax = null;
}
public function SetBoundaries($iMin = null, $iMax = null)
{
$this->iMin = $iMin;
$this->iMax = $iMax;
}
public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
{
$sId = $this->oForm->GetFieldId($this->sCode);
$sName = $this->oForm->GetFieldName($this->sCode);
if ($this->IsReadOnly())
{
$sHtmlValue = "<span>".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."\"/></span>";
}
else
{
$sMin = json_encode($this->iMin);
$sMax = json_encode($this->iMax);
$sMandatory = $this->bMandatory ? 'true' : 'false';
$oP->add_ready_script(
<<<EOF
$('#$sId').bind('change keyup validate', function() { ValidateInteger('$sId', $sMandatory, '$sFormId', $sMin, $sMax); } );
{
var myTimer = null;
$('#$sId').bind('keyup', function() { clearTimeout(myTimer); myTimer = setTimeout(function() { $('#$sId').trigger('change', {} ); }, 100); });
}
EOF
);
$sCSSClasses = '';
if (count($this->aCSSClasses) > 0)
{
$sCSSClasses = 'class="'.implode(' ', $this->aCSSClasses).'"';
}
$sHtmlValue = "<input type=\"text\" $sCSSClasses id=\"$sId\" name=\"$sName\" value=\"".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."\">";
}
return array('label' => $this->sLabel, 'value' => $sHtmlValue);
}
public function ReadParam(&$aValues)
{
parent::ReadParam($aValues);
if (!is_null($this->iMin) && ($aValues[$this->sCode] < $this->iMin))
{
// Reject the value...
$aValues[$this->sCode] = $this->defaultValue;
}
if (!is_null($this->iMax) && ($aValues[$this->sCode] > $this->iMax))
{
// Reject the value...
$aValues[$this->sCode] = $this->defaultValue;
}
}
}
class DesignerComboField extends DesignerFormField
{
protected $aAllowedValues;

View File

@@ -305,16 +305,20 @@ class LoginWebPage extends NiceWebPage
{
$this->add("<p>".Dict::Format('UI:ResetPwd-Error-WrongLogin', $sAuthUser)."</p>\n");
}
elseif ($oUser->Get('reset_pwd_token') != $sToken)
{
$this->add("<p>".Dict::S('UI:ResetPwd-Error-InvalidToken')."</p>\n");
}
else
{
$this->add("<p>".Dict::Format('UI:ResetPwd-Error-EnterPassword', $oUser->GetFriendlyName())."</p>\n");
$sInconsistenPwdMsg = Dict::S('UI:Login:RetypePwdDoesNotMatch');
$this->add_script(
$oEncryptedToken = $oUser->Get('reset_pwd_token');
if (!$oEncryptedToken->CheckPassword($sToken))
{
$this->add("<p>".Dict::S('UI:ResetPwd-Error-InvalidToken')."</p>\n");
}
else
{
$this->add("<p>".Dict::Format('UI:ResetPwd-Error-EnterPassword', $oUser->GetFriendlyName())."</p>\n");
$sInconsistenPwdMsg = Dict::S('UI:Login:RetypePwdDoesNotMatch');
$this->add_script(
<<<EOF
function DoCheckPwd()
{
@@ -326,18 +330,19 @@ function DoCheckPwd()
return true;
}
EOF
);
$this->add("<form method=\"post\">\n");
$this->add("<table>\n");
$this->add("<tr><td style=\"text-align:right\"><label for=\"new_pwd\">".Dict::S('UI:Login:NewPasswordPrompt').":</label></td><td style=\"text-align:left\"><input type=\"password\" id=\"new_pwd\" name=\"new_pwd\" value=\"\" /></td></tr>\n");
$this->add("<tr><td style=\"text-align:right\"><label for=\"retype_new_pwd\">".Dict::S('UI:Login:RetypeNewPasswordPrompt').":</label></td><td style=\"text-align:left\"><input type=\"password\" id=\"retype_new_pwd\" name=\"retype_new_pwd\" value=\"\" /></td></tr>\n");
$this->add("<tr><td colspan=\"2\" class=\"center v-spacer\"><span class=\"btn_border\"><input type=\"submit\" onClick=\"return DoCheckPwd();\" value=\"".Dict::S('UI:Button:ChangePassword')."\" /></span></td></tr>\n");
$this->add("</table>\n");
$this->add("<input type=\"hidden\" name=\"loginop\" value=\"do_reset_pwd\" />\n");
$this->add("<input type=\"hidden\" name=\"auth_user\" value=\"".htmlentities($sAuthUser, ENT_QUOTES, 'UTF-8')."\" />\n");
$this->add("<input type=\"hidden\" name=\"token\" value=\"".htmlentities($sToken, ENT_QUOTES, 'UTF-8')."\" />\n");
$this->add("</form>\n");
$this->add("</div\n");
);
$this->add("<form method=\"post\">\n");
$this->add("<table>\n");
$this->add("<tr><td style=\"text-align:right\"><label for=\"new_pwd\">".Dict::S('UI:Login:NewPasswordPrompt').":</label></td><td style=\"text-align:left\"><input type=\"password\" id=\"new_pwd\" name=\"new_pwd\" value=\"\" /></td></tr>\n");
$this->add("<tr><td style=\"text-align:right\"><label for=\"retype_new_pwd\">".Dict::S('UI:Login:RetypeNewPasswordPrompt').":</label></td><td style=\"text-align:left\"><input type=\"password\" id=\"retype_new_pwd\" name=\"retype_new_pwd\" value=\"\" /></td></tr>\n");
$this->add("<tr><td colspan=\"2\" class=\"center v-spacer\"><span class=\"btn_border\"><input type=\"submit\" onClick=\"return DoCheckPwd();\" value=\"".Dict::S('UI:Button:ChangePassword')."\" /></span></td></tr>\n");
$this->add("</table>\n");
$this->add("<input type=\"hidden\" name=\"loginop\" value=\"do_reset_pwd\" />\n");
$this->add("<input type=\"hidden\" name=\"auth_user\" value=\"".htmlentities($sAuthUser, ENT_QUOTES, 'UTF-8')."\" />\n");
$this->add("<input type=\"hidden\" name=\"token\" value=\"".htmlentities($sToken, ENT_QUOTES, 'UTF-8')."\" />\n");
$this->add("</form>\n");
$this->add("</div\n");
}
}
}
@@ -357,21 +362,25 @@ EOF
{
$this->add("<p>".Dict::Format('UI:ResetPwd-Error-WrongLogin', $sAuthUser)."</p>\n");
}
elseif ($oUser->Get('reset_pwd_token') != $sToken)
{
$this->add("<p>".Dict::S('UI:ResetPwd-Error-InvalidToken')."</p>\n");
}
else
{
// Trash the token and change the password
$oUser->Set('reset_pwd_token', '');
$oUser->SetPassword($sNewPwd); // Does record the change into the DB
$this->add("<p>".Dict::S('UI:ResetPwd-Ready')."</p>");
$sUrl = utils::GetAbsoluteUrlAppRoot();
$this->add("<p><a href=\"$sUrl\">".Dict::S('UI:ResetPwd-Login')."</a></p>");
$oEncryptedPassword = $oUser->Get('reset_pwd_token');
if (!$oEncryptedPassword->CheckPassword($sToken))
{
$this->add("<p>".Dict::S('UI:ResetPwd-Error-InvalidToken')."</p>\n");
}
else
{
// Trash the token and change the password
$oUser->Set('reset_pwd_token', '');
$oUser->SetPassword($sNewPwd); // Does record the change into the DB
$this->add("<p>".Dict::S('UI:ResetPwd-Ready')."</p>");
$sUrl = utils::GetAbsoluteUrlAppRoot();
$this->add("<p><a href=\"$sUrl\">".Dict::S('UI:ResetPwd-Login')."</a></p>");
}
$this->add("</div\n");
}
$this->add("</div\n");
}
public function DisplayChangePwdForm($bFailedLogin = false)

View File

@@ -254,8 +254,8 @@ class ShortcutOQL extends Shortcut
$oField = new DesignerBooleanField('auto_reload', Dict::S('Class:ShortcutOQL/Attribute:auto_reload'), false);
$oForm->AddField($oField);
$oField = new DesignerTextField('auto_reload_sec', Dict::S('Class:ShortcutOQL/Attribute:auto_reload_sec'), MetaModel::GetConfig()->GetStandardReloadInterval());
$oField->SetValidationPattern('^$|^0*([5-9]|[1-9][0-9]+)$'); // Can be empty, or a number > 4
$oField = new DesignerIntegerField('auto_reload_sec', Dict::S('Class:ShortcutOQL/Attribute:auto_reload_sec'), MetaModel::GetConfig()->GetStandardReloadInterval());
$oField->SetBoundaries(MetaModel::GetConfig()->Get('min_reload_interval'), null); // no upper limit
$oField->SetMandatory(false);
$oForm->AddField($oField);
@@ -284,7 +284,7 @@ class ShortcutOQL extends Shortcut
$oAppContext = new ApplicationContext();
$sContext = $oAppContext->GetForLink();
$sRateTitle = addslashes(Dict::S('Class:ShortcutOQL/Attribute:auto_reload_sec+'));
$sRateTitle = addslashes(Dict::Format('Class:ShortcutOQL/Attribute:auto_reload_sec/tip', MetaModel::GetConfig()->Get('min_reload_interval')));
$oPage->add_ready_script(
<<<EOF

View File

@@ -28,10 +28,11 @@ require_once(APPROOT.'/core/cmdbobject.class.inc.php');
require_once(APPROOT.'/application/utils.inc.php');
session_name('itop-'.md5(APPROOT));
session_start();
if (isset($_REQUEST['switch_env']))
$sSwitchEnv = utils::ReadParam('switch_env', null);
if (($sSwitchEnv != null) && (file_exists(APPCONF.$sSwitchEnv.'/'.ITOP_CONFIG_FILE)))
{
$sEnv = $_REQUEST['switch_env'];
$_SESSION['itop_env'] = $sEnv;
$_SESSION['itop_env'] = $sSwitchEnv;
$sEnv = $sSwitchEnv;
// TODO: reset the credentials as well ??
}
else if (isset($_SESSION['itop_env']))

View File

@@ -239,7 +239,7 @@ EOF
*/
protected function DisplayFormTable(WebPage $oP, $aConfig, $aData)
{
$sHtml = '';
$sHtml = "<input type=\"hidden\" name=\"attr_{$this->m_sAttCode}{$this->m_sNameSuffix}\" value=\"\">";
$sHtml .= "<table class=\"listResults\">\n";
// Header
$sHtml .= "<thead>\n";
@@ -259,11 +259,11 @@ EOF
$sEmptyRowStyle = 'style="display:none;"';
}
$sHtml .= "<tr $sEmptyRowStyle id=\"{$this->m_sAttCode}{$this->m_sNameSuffix}_empty_row\"><td colspan=\"".count($aConfig)."\" style=\"text-align:center;\">".Dict::S('UI:Message:EmptyList:UseAdd')."<input type=\"hidden\" name=\"attr_{$this->m_sAttCode}{$this->m_sNameSuffix}\" value=\"\"></td></td>";
foreach($aData as $iRowId => $aRow)
{
$sHtml .= $this->DisplayFormRow($oP, $aConfig, $aRow, $iRowId);
}
$sHtml .= "<tr $sEmptyRowStyle id=\"{$this->m_sAttCode}{$this->m_sNameSuffix}_empty_row\"><td colspan=\"".count($aConfig)."\" style=\"text-align:center;\">".Dict::S('UI:Message:EmptyList:UseAdd')."</td></tr>";
$sHtml .= "</tbody>\n";
// Footer

View File

@@ -91,6 +91,9 @@ abstract class AsyncTask extends DBObject
MetaModel::Init_AddAttribute(new AttributeExternalKey("event_id", array("targetclass"=>"Event", "jointype"=> "", "allowed_values"=>null, "sql"=>"event_id", "is_null_allowed"=>true, "on_target_delete"=>DEL_SILENT, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("remaining_retries", array("allowed_values"=>null, "sql"=>"remaining_retries", "default_value"=>0, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("last_error_code", array("allowed_values"=>null, "sql"=>"last_error_code", "default_value"=>0, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("last_error", array("allowed_values"=>null, "sql"=>"last_error", "default_value"=>'', "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("last_attempt", array("allowed_values"=>null, "sql"=>"last_attempt", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
}
/**
@@ -117,27 +120,34 @@ abstract class AsyncTask extends DBObject
{
try
{
$this->Set('status', 'running');
$this->Set('started', time());
$this->DBUpdate();
return self::OK;
if ($this->Get('status') == 'running')
{
return self::ALREADY_RUNNING;
}
else
{
$this->Set('status', 'running');
$this->Set('started', time());
$this->DBUpdate();
return self::OK;
}
}
catch(Exception $e)
{
// Corrupted task !! (for example: "Failed to reload object")
IssueLog::Error('Failed to process async task #'.$this->GetKey().' - reason: '.$e->getMessage().' - fatal error, deleting the task.');
if ($this->Get('event_id') != 0)
{
$oEventLog = MetaModel::GetObject('Event', $this->Get('event_id'));
$oEventLog->Set('message', 'Failed, corrupted data: '.$e->getMessage());
$oEventLog->DBUpdate();
if ($this->Get('event_id') != 0)
{
$oEventLog = MetaModel::GetObject('Event', $this->Get('event_id'));
$oEventLog->Set('message', 'Failed, corrupted data: '.$e->getMessage());
$oEventLog->DBUpdate();
}
$this->DBDelete();
return self::DELETED;
}
}
public function GetRetryDelay()
public function GetRetryDelay($iErrorCode = null)
{
$iRetryDelay = 600;
$aRetries = MetaModel::GetConfig()->Get('async_task_retries', array());
@@ -149,7 +159,7 @@ abstract class AsyncTask extends DBObject
return $iRetryDelay;
}
public function GetMaxRetries()
public function GetMaxRetries($iErrorCode = null)
{
$iMaxRetries = 0;
$aRetries = MetaModel::GetConfig()->Get('async_task_retries', array());
@@ -160,10 +170,16 @@ abstract class AsyncTask extends DBObject
}
}
/**
* Override to notify people that a task cannot be performed
*/
protected function OnDefinitiveFailure()
{
}
protected function OnInsert()
{
$this->Set('created', time());
$this->Set('remaining_retries', $this->GetMaxRetries());
}
/**
@@ -191,22 +207,7 @@ abstract class AsyncTask extends DBObject
}
catch(Exception $e)
{
$iRemaining = $this->Get('remaining_retries');
if ($iRemaining > 0)
{
$iRetryDelay = $this->GetRetryDelay();
IssueLog::Info('Failed to process async task #'.$this->GetKey().' - reason: '.$e->getMessage().' - remaining retries: '.$iRemaining.' - next retry in '.$iRetryDelay.'s');
$this->Set('remaining_retries', $iRemaining - 1);
$this->Set('status', 'planned');
$this->Set('started', null);
$this->Set('planned', time() + $iRetryDelay);
$this->DBUpdate();
}
else
{
IssueLog::Error('Failed to process async task #'.$this->GetKey().' - reason: '.$e->getMessage());
}
$this->HandleError($e->getMessage(), $e->getCode());
}
}
else
@@ -217,7 +218,56 @@ abstract class AsyncTask extends DBObject
return $bRet;
}
/**
* Overridable to extend the behavior in case of error (logging)
*/
protected function HandleError($sErrorMessage, $iErrorCode)
{
if ($this->Get('last_attempt') == '')
{
// First attempt
$this->Set('remaining_retries', $this->GetMaxRetries($iErrorCode));
}
$this->Set('last_error', $sErrorMessage);
$this->Set('last_error_code', $iErrorCode); // Note: can be ZERO !!!
$this->Set('last_attempt', time());
$iRemaining = $this->Get('remaining_retries');
if ($iRemaining > 0)
{
$iRetryDelay = $this->GetRetryDelay($iErrorCode);
IssueLog::Info('Failed to process async task #'.$this->GetKey().' - reason: '.$sErrorMessage.' - remaining retries: '.$iRemaining.' - next retry in '.$iRetryDelay.'s');
$this->Set('remaining_retries', $iRemaining - 1);
$this->Set('status', 'planned');
$this->Set('started', null);
$this->Set('planned', time() + $iRetryDelay);
}
else
{
IssueLog::Error('Failed to process async task #'.$this->GetKey().' - reason: '.$sErrorMessage);
$this->Set('status', 'error');
$this->Set('started', null);
$this->Set('planned', null);
$this->OnDefinitiveFailure();
}
$this->DBUpdate();
}
/**
* Throws an exception (message and code)
*/
abstract public function DoProcess();
/**
* Describes the error codes that DoProcess can return by the mean of exceptions
*/
static public function EnumErrorCodes()
{
return array();
}
}
/**

View File

@@ -432,7 +432,7 @@ class BulkChange
break;
default:
$aErrors[$sAttCode] = Dict::Format('UI:CSVReport-Value-Issue-FoundMany', $oExtObjects->Count());
$aErrors[$sAttCode] = Dict::Format('UI:CSVReport-Value-Issue-FoundMany', $iCount);
$aResults[$sAttCode]= new CellStatus_Ambiguous($oTargetObj->Get($sAttCode), $iCount, $sOQL);
}
}

View File

@@ -428,38 +428,38 @@ abstract class CMDBObject extends DBObject
}
public function DBInsert()
{
return $this->DBInsertTracked_Internal();
}
public function DBInsertTracked(CMDBChange $oChange, $bSkipStrongSecurity = null)
{
self::SetCurrentChange($oChange);
$this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_MODIFY);
$ret = $this->DBInsertTracked_Internal();
return $ret;
}
public function DBInsertTrackedNoReload(CMDBChange $oChange, $bSkipStrongSecurity = null)
{
self::SetCurrentChange($oChange);
$this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_MODIFY);
$ret = $this->DBInsertTracked_Internal(true);
return $ret;
}
/**
* To Be Obsoleted: DO NOT rely on an overload of this method since
* DBInsertTracked (resp. DBInsertTrackedNoReload) may call directly
* DBInsert (resp. DBInsertNoReload) in future versions of iTop.
* @param bool $bDoNotReload
* @return integer Identifier of the created object
*/
protected function DBInsertTracked_Internal($bDoNotReload = false)
{
if ($bDoNotReload)
{
$ret = parent::DBInsertNoReload();
$ret = $this->DBInsertNoReload();
}
else
{
$ret = parent::DBInsert();
$ret = $this->DBInsert();
}
return $ret;
}

View File

@@ -79,7 +79,7 @@ class CMDBSource
// Override the default port
$sServer = $aConnectInfo[0];
$iPort = (int)$aConnectInfo[1];
self::$m_oMysqli = new mysqli(self::$m_sDBHost, self::$m_sDBUser, self::$m_sDBPwd, '', $iPort);
self::$m_oMysqli = new mysqli($sServer, self::$m_sDBUser, self::$m_sDBPwd, '', $iPort);
}
else
{

View File

@@ -769,6 +769,14 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'min_reload_interval' => array(
'type' => 'integer',
'description' => 'Minimum refresh interval (seconds) for dashboards, shortcuts, etc. Even if the interval is set programmatically, it is forced to that minimum',
'default' => 5, // In iTop 2.0.3, this was the hardcoded value
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
),
);
public function IsProperty($sPropCode)

View File

@@ -654,7 +654,11 @@ class DBObjectSet
else
{
// Pick the row from the objects added *in memory*
$oRetObj = $this->m_aAddedObjects[$this->m_iCurrRow - $this->m_iNumLoadedDBRows][$sRequestedClassAlias];
$aRetObjects = array();
foreach ($this->m_oFilter->GetSelectedClasses() as $sClassAlias => $sClass)
{
$aRetObjects[$sClassAlias] = $this->m_aAddedObjects[$this->m_iCurrRow - $this->m_iNumLoadedDBRows][$sClassAlias];
}
}
$this->m_iCurrRow++;
return $aRetObjects;
@@ -1094,8 +1098,11 @@ class DBObjectSet
{
foreach($aVals as $sCode => $oExpr)
{
$oScalarExpr = $oExpr->GetAsScalar($aScalarArgs);
$aConst[$sClassAlias][$sCode] = $oScalarExpr->GetValue();
if (is_object($oExpr)) // Array_merge_recursive creates an array when the same key is present multiple times... ignore them
{
$oScalarExpr = $oExpr->GetAsScalar($aScalarArgs);
$aConst[$sClassAlias][$sCode] = $oScalarExpr->GetValue();
}
}
}
return $aConst;

View File

@@ -351,7 +351,7 @@ class EventLoginUsage extends Event
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeExternalKey("user_id", array("targetclass"=>"User", "jointype"=> "", "allowed_values"=>null, "sql"=>"user_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalKey("user_id", array("targetclass"=>"User", "jointype"=> "", "allowed_values"=>null, "sql"=>"user_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_SILENT, "depends_on"=>array())));
$aZList = array('date', 'user_id');
if (MetaModel::IsValidAttCode('Contact', 'name'))
{

View File

@@ -299,14 +299,11 @@ class BinaryExpression extends Expression
{
$aResult[$this->m_oRightExpr->GetParent()][$this->m_oRightExpr->GetName()] = $this->m_oLeftExpr;
}
else
{
$aResult = array_merge($this->m_oRightExpr->ListConstantFields(), $this->m_oLeftExpr->ListConstantFields()) ;
}
}
else
else if ($this->m_sOperator == 'AND')
{
$aResult = array_merge($this->m_oRightExpr->ListConstantFields(), $this->m_oLeftExpr->ListConstantFields()) ;
// Strictly, this should be done only for the AND operator
$aResult = array_merge_recursive($this->m_oRightExpr->ListConstantFields(), $this->m_oLeftExpr->ListConstantFields());
}
return $aResult;
}

View File

@@ -835,6 +835,10 @@ abstract class MetaModel
final static public function GetAttributeDef($sClass, $sAttCode)
{
self::_check_subclass($sClass);
if (!isset(self::$m_aAttribDefs[$sClass][$sAttCode]))
{
throw new Exception("Unknown attribute $sAttCode from class $sClass");
}
return self::$m_aAttribDefs[$sClass][$sAttCode];
}

View File

@@ -384,7 +384,7 @@ class ormCaseLog {
}
public function AddLogEntryFromJSON($oJson)
public function AddLogEntryFromJSON($oJson, $bCheckUserId = true)
{
$sText = isset($oJson->message) ? $oJson->message : '';
@@ -394,16 +394,24 @@ class ormCaseLog {
{
throw new Exception("Only administrators can set the user id", RestResult::UNAUTHORIZED);
}
try
if ($bCheckUserId)
{
$oUser = RestUtils::FindObjectFromKey('User', $oJson->user_id);
try
{
$oUser = RestUtils::FindObjectFromKey('User', $oJson->user_id);
}
catch(Exception $e)
{
throw new Exception('user_id: '.$e->getMessage(), $e->getCode());
}
$iUserId = $oUser->GetKey();
$sOnBehalfOf = $oUser->GetFriendlyName();
}
catch(Exception $e)
else
{
throw new Exception('user_id: '.$e->getMessage(), $e->getCode());
$iUserId = $oJson->user_id;
$sOnBehalfOf = $oJson->user_login;
}
$iUserId = $oUser->GetKey();
$sOnBehalfOf = $oUser->GetFriendlyName();
}
else
{

View File

@@ -371,7 +371,7 @@ abstract class UserInternal extends User
MetaModel::Init_InheritAttributes();
// When set, this token allows for password reset
MetaModel::Init_AddAttribute(new AttributeString("reset_pwd_token", array("allowed_values"=>null, "sql"=>"reset_pwd_token", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeOneWayPassword("reset_pwd_token", array("allowed_values"=>null, "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('contactid', 'first_name', 'email', 'login', 'language', 'profile_list', 'allowed_org_list')); // Attributes to be displayed for the complete details

View File

@@ -440,7 +440,6 @@ EOF
);
$oPage->p('<span style="display:none;" id="attachment_loading">Loading, please wait...</span>');
$oPage->p('<input type="hidden" id="attachment_plugin" name="attachment_plugin"/>');
$oPage->add('</fieldset>');
if ($this->m_bDeleteEnabled)
{
$oPage->add_ready_script('$(".attachment").hover( function() {$(this).children(":button").toggleClass("btn_hidden"); } );');
@@ -466,7 +465,9 @@ EOF
$oPage->add('<div class="attachment" id="attachment_'.$iAttId.'"><a data-preview="'.$sPreview.'" href="'.$sDownloadLink.'"><img src="'.$sIcon.'"><br/>'.$sFileName.'</a><input type="hidden" name="attachments[]" value="'.$iAttId.'"/><br/>&nbsp;&nbsp;</div>');
}
}
$oPage->add('</span>');
}
$oPage->add('</fieldset>');
$sPreviewNotAvailable = addslashes(Dict::S('Attachments:PreviewNotAvailable'));
$oPage->add_ready_script("$(document).tooltip({ items: '.attachment a', position: { my: 'left top', at: 'right top', using: function( position, feedback ) { $( this ).css( position ); }}, content: function() { if ($(this).attr('data-preview') == 'true') { return('<img style=\"max-width:290px\" src=\"'+$(this).attr('href')+'\"></img>');} else { return '$sPreviewNotAvailable'; }}});");
}

View File

@@ -1093,7 +1093,7 @@ When associated with a trigger, each action is given an "order" number, specifyi
'UI:DashboardEdit:DashboardTitle' => 'Title',
'UI:DashboardEdit:AutoReload' => 'Automatic refresh',
'UI:DashboardEdit:AutoReloadSec' => 'Automatic refresh interval (seconds)',
'UI:DashboardEdit:AutoReloadSec+' => 'The minimum allowed is 5 seconds',
'UI:DashboardEdit:AutoReloadSec+' => 'The minimum allowed is %1$d seconds',
'UI:DashboardEdit:Layout' => 'Layout',
'UI:DashboardEdit:Properties' => 'Dashboard Properties',
@@ -1193,7 +1193,7 @@ When associated with a trigger, each action is given an "order" number, specifyi
'Class:ShortcutOQL/Attribute:auto_reload/Value:none' => 'Disabled',
'Class:ShortcutOQL/Attribute:auto_reload/Value:custom' => 'Custom rate',
'Class:ShortcutOQL/Attribute:auto_reload_sec' => 'Automatic refresh interval (seconds)',
'Class:ShortcutOQL/Attribute:auto_reload_sec+' => 'The minimum allowed is 5 seconds',
'Class:ShortcutOQL/Attribute:auto_reload_sec/tip' => 'The minimum allowed is %1$d seconds',
'UI:FillAllMandatoryFields' => 'Please fill all mandatory fields.',

View File

@@ -1085,7 +1085,7 @@ Cuando se asocien con un disparador, cada acción recibe un número de "orden",
'UI:DashboardEdit:DashboardTitle' => 'Título',
'UI:DashboardEdit:AutoReload' => 'Actualización Automática',
'UI:DashboardEdit:AutoReloadSec' => 'Interválo de Actualización Automática (segundos)',
'UI:DashboardEdit:AutoReloadSec+' => 'El interválo mínimo es de 5 segundos',
'UI:DashboardEdit:AutoReloadSec+' => 'El interválo mínimo es de %1$d segundos',
'UI:DashboardEdit:Layout' => 'Distribución',
'UI:DashboardEdit:Properties' => 'Propiedades',
'UI:DashboardEdit:Dashlets' => 'Dashlets disponibles',
@@ -1184,7 +1184,7 @@ Cuando se asocien con un disparador, cada acción recibe un número de "orden",
'Class:ShortcutOQL/Attribute:auto_reload/Value:none' => 'Deshabilitado',
'Class:ShortcutOQL/Attribute:auto_reload/Value:custom' => 'Frecuencia configurable',
'Class:ShortcutOQL/Attribute:auto_reload_sec' => 'Interválo de Actualización Automática (segundos)',
'Class:ShortcutOQL/Attribute:auto_reload_sec+' => 'El interválo mínimo es de 5 segundos',
'Class:ShortcutOQL/Attribute:auto_reload_sec/tip' => 'El interválo mínimo es de %1$d segundos',
'UI:FillAllMandatoryFields' => 'Por favor llenar los campos obligatorios.',
'UI:CSVImportConfirmTitle' => 'Por favor confirme la operación',
'UI:CSVImportConfirmMessage' => '¿Está seguro?',

View File

@@ -934,7 +934,7 @@ Lors de l\'association à un déclencheur, on attribue à chaque action un numé
'UI:DashboardEdit:DashboardTitle' => 'Titre',
'UI:DashboardEdit:AutoReload' => 'Réactualisation automatique',
'UI:DashboardEdit:AutoReloadSec' => 'Réactualisation toutes les (secondes)',
'UI:DashboardEdit:AutoReloadSec+' => 'Le minimum permis est de 5 secondes',
'UI:DashboardEdit:AutoReloadSec+' => 'Le minimum permis est de %1$d secondes',
'UI:DashboardEdit:Layout' => 'Mise en page',
'UI:DashboardEdit:Properties' => 'Propriétés du tableau de bord',
'UI:DashboardEdit:Dashlets' => 'Indicateurs',
@@ -1033,7 +1033,7 @@ Lors de l\'association à un déclencheur, on attribue à chaque action un numé
'Class:ShortcutOQL/Attribute:auto_reload/Value:none' => 'Désactivée',
'Class:ShortcutOQL/Attribute:auto_reload/Value:custom' => 'Personnalisée',
'Class:ShortcutOQL/Attribute:auto_reload_sec' => 'Réactualisation toutes les (secondes)',
'Class:ShortcutOQL/Attribute:auto_reload_sec+' => 'Le minimum permis est de 5 secondes',
'Class:ShortcutOQL/Attribute:auto_reload_sec/tip' => 'Le minimum permis est de %1$d secondes',
'UI:FillAllMandatoryFields' => 'Veuillez remplir tous les champs obligatoires.',

View File

@@ -1084,7 +1084,7 @@ When associated with a trigger, each action is given an "order" number, specifyi
'UI:DashboardEdit:DashboardTitle' => 'Título',
'UI:DashboardEdit:AutoReload' => 'Atualizar automaticamente',
'UI:DashboardEdit:AutoReloadSec' => 'Intervalo atualização automática (segundos)',
'UI:DashboardEdit:AutoReloadSec+' => 'O mínimo permitido é 5 segundos',
'UI:DashboardEdit:AutoReloadSec+' => 'O mínimo permitido é %1$d segundos',
'UI:DashboardEdit:Layout' => 'Layout',
'UI:DashboardEdit:Properties' => 'Propriedades',
@@ -1184,7 +1184,7 @@ When associated with a trigger, each action is given an "order" number, specifyi
'Class:ShortcutOQL/Attribute:auto_reload/Value:none' => 'Desabilitado',
'Class:ShortcutOQL/Attribute:auto_reload/Value:custom' => 'Avaliar',
'Class:ShortcutOQL/Attribute:auto_reload_sec' => 'Intervalo atualização automática (segundos)',
'Class:ShortcutOQL/Attribute:auto_reload_sec+' => 'O mínimo permitido é 5 sgundos',
'Class:ShortcutOQL/Attribute:auto_reload_sec/tip' => 'O mínimo permitido é %1$d sgundos',
'UI:FillAllMandatoryFields' => 'Por favor, preencha todos os campos obrigatórios.',
'UI:CSVImportConfirmTitle' => 'Por favor, confirme a operação',

View File

@@ -1077,7 +1077,7 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
'UI:DashboardEdit:DashboardTitle' => 'Заголовок',
'UI:DashboardEdit:AutoReload' => 'Обновлять автоматически',
'UI:DashboardEdit:AutoReloadSec' => 'Интервал обновления (секунды)',
'UI:DashboardEdit:AutoReloadSec+' => 'Минимальный интервал 5 секунд',
'UI:DashboardEdit:AutoReloadSec+' => 'Минимальный интервал %1$d секунд',
'UI:DashboardEdit:Layout' => 'Макет',
'UI:DashboardEdit:Properties' => 'Свойства дашборда',
@@ -1177,7 +1177,7 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:ShortcutOQL/Attribute:auto_reload/Value:none' => 'Disabled',
'Class:ShortcutOQL/Attribute:auto_reload/Value:custom' => 'Custom rate',
'Class:ShortcutOQL/Attribute:auto_reload_sec' => 'Automatic refresh interval (seconds)',
'Class:ShortcutOQL/Attribute:auto_reload_sec+' => 'The minimum allowed is 5 seconds',
'Class:ShortcutOQL/Attribute:auto_reload_sec/tip' => 'The minimum allowed is %1$d seconds',
'UI:FillAllMandatoryFields' => 'Пожалуйста, заполните все обязательные поля.',

View File

@@ -139,7 +139,7 @@ $(function()
this.datatable.tableHover();
this.datatable.find('.selectList'+this.id).bind('change', function() { me._updateButtons(); });
},
_updateDlgSize: function()
_updateDlgPosition: function()
{
this.oDlg.dialog('option', { position: { my: "center", at: "center", of: window }});
},
@@ -171,7 +171,7 @@ $(function()
});
me.indicator.html('');
me.oButtons['create'].removeAttr('disabled');
me._updateDlgSize();
me._updateDlgPosition();
});
},
_selectToAdd: function()
@@ -197,20 +197,23 @@ $(function()
me.oDlg.find('form').removeAttr('onsubmit').bind('submit', function() { me._onSearchToAdd(); return false; } );
me.oDlg.find('button.cancel').unbind('click').click( function() { me.oDlg.dialog('close'); } );
me.oDlg.find('button.ok').unbind('click').click( function() { me._onDoAdd(); } );
$('#SearchFormToAdd_'+me.id).resize(function() { me._onSearchDlgUpdateSize(); });
me.oDlg.dialog({
title: me.options.labels['selection_title'],
modal: true,
width: 'auto',
height: 'auto',
width: $(window).width()*0.8,
height: $(window).height()*0.8,
maxHeight: $(window).height() - 50,
position: { my: "center", at: "center", of: window },
close: function() { me._onDlgClose(); }
close: function() { me._onDlgClose(); },
resizeStop: function() { me._onSearchDlgUpdateSize(); }
});
me.indicator.html('');
me.oButtons['add'].removeAttr('disabled');
me._onSearchToAdd();
me._updateDlgSize();
me._updateDlgPosition();
me._onSearchDlgUpdateSize();
});
},
_onSearchToAdd: function()
@@ -255,8 +258,8 @@ $(function()
me._onUpdateDlgButtons(c);
});
$('#SearchResultsToAdd_'+me.id).unblock();
me._onSearchDlgUpdateSize();
});
//alert("C'est parti mon kiki !");
return false; // Stay on the page, no submit
},
_getSelection: function(sName)
@@ -373,7 +376,7 @@ $(function()
me.oDlg.html(data);
me.oDlg.find('form').removeAttr('onsubmit').bind('submit', function() { me._onCreateRow(); return false; } );
me.oDlg.find('button.cancel').unbind('click').click( function() { me.oDlg.dialog('close'); } );
me._updateDlgSize();
me._updateDlgPosition();
});
},
_onCreateRow: function()
@@ -426,6 +429,13 @@ $(function()
this.oDlg.remove();
this.oDlg = null;
},
_onSearchDlgUpdateSize: function()
{
var searchHeight = $('#SearchFormToAdd_'+this.id).outerHeight();
var dlgHeight = this.oDlg.height();
$('.wizContainer', this.oDlg).height(dlgHeight - 20);
$('#SearchResultsToAdd_'+this.id).height(dlgHeight - 50 - searchHeight);
},
_deleteRow: function(oCheckbox)
{
var iObjKey = parseInt(oCheckbox.val(), 10); // Number in base 10

View File

@@ -242,7 +242,7 @@ function LinksWidget(id, sClass, sAttCode, iInputId, sSuffix, bDuplicates, oWizH
if (data != '')
{
$('#'+me.id+'_empty_row').hide();
$('#linkedset_'+me.id+' .listResults tbody').append(data);
$('#linkedset_'+me.id+' .listResults tbody').prepend(data);
$('#linkedset_'+me.id+' .listResults').trigger('update');
$('#linkedset_'+me.id+' .listResults').tableHover();
$('#linkedset_'+me.id+' .listResults').trigger('update').trigger("applyWidgets"); // table is already sortable, just refresh it

View File

@@ -272,6 +272,77 @@ function ValidateWithPattern(sFieldId, bMandatory, sPattern, sFormId, aForbidden
}
}
function ValidateInteger(sFieldId, bMandatory, sFormId, iMin, iMax, sExplainFormat)
{
var currentVal = $('#'+sFieldId).val();
var bValid = true;
var sMessage = null;
if (bMandatory && (currentVal == ''))
{
bValid = false;
}
re = new RegExp('^$|^-?[0-9]+$');
bValid = re.test(currentVal);
if (bValid && (currentVal != ''))
{
// It is a valid number, let's check the boundaries
var iValue = parseInt(currentVal, 10);
if ((iMin != null) && (iValue < iMin))
{
bValid = false;
}
if ((iMax != null) && (iValue > iMax))
{
bValid = false;
}
if (!bValid && (sExplainFormat != undefined))
{
sMessage = sExplainFormat;
}
}
if (oFormValidation[sFormId] == undefined) oFormValidation[sFormId] = [];
if (!bValid)
{
$('#v_'+sFieldId).addClass('ui-state-error');
iFieldIdPos = jQuery.inArray(sFieldId, oFormValidation[sFormId]);
if (iFieldIdPos == -1)
{
oFormValidation[sFormId].push(sFieldId);
}
if (sMessage)
{
$('#'+sFieldId).attr('title', sMessage).tooltip();
if ($('#'+sFieldId).is(":focus"))
{
$('#'+sFieldId).tooltip('open');
}
}
}
else
{
$('#v_'+sFieldId).removeClass('ui-state-error');
if ($('#'+sFieldId).data('uiTooltip'))
{
$('#'+sFieldId).tooltip('close');
}
$('#'+sFieldId).removeAttr('title');
// Remove the element from the array
iFieldIdPos = jQuery.inArray(sFieldId, oFormValidation[sFormId]);
if (iFieldIdPos > -1)
{
oFormValidation[sFormId].splice(iFieldIdPos, 1);
}
}
}
function ValidateForm(sFormId, bValidateAll)
{
oFormValidation[sFormId] = [];

View File

@@ -834,6 +834,7 @@ try
$oMenu = ApplicationMenu::GetMenuNode($idx);
$oDashboard = $oMenu->GetDashboard();
$oDashboard->Render($oPage, false, $aExtraParams);
$oPage->add_ready_script("$('.dashboard_contents table.listResults').tableHover(); $('.dashboard_contents table.listResults').tablesorter( { widgets: ['myZebra', 'truncatedList']} );");
break;
case 'dashboard_editor':
@@ -1019,7 +1020,7 @@ EOF
$iAutoReload = (int)$aValues['auto_reload_sec'];
if (($aValues['auto_reload']) && ($iAutoReload > 0))
{
$oShortcut->Set("auto_reload_sec", max(5, $iAutoReload));
$oShortcut->Set("auto_reload_sec", max(MetaModel::GetConfig()->Get('min_reload_interval'), $iAutoReload));
$oShortcut->Set("auto_reload", 'custom');
}
$iId = $oShortcut->DBInsertNoReload();

View File

@@ -180,15 +180,15 @@ try
if ($sFileName != null)
{
$oP = new CSVPage("iTop - Export");
$sCSVData = cmdbAbstractObject::GetSetAsCSV($oErrorObjectSet, array('localize_values' => true, 'fields_advanced' => $bAdvanced));
$sCharset = MetaModel::GetConfig()->Get('csv_file_default_charset');
$sCSVData = cmdbAbstractObject::GetSetAsCSV($oErrorObjectSet, array('localize_values' => true, 'fields_advanced' => $bAdvanced), $sCharset);
if ($sCharset == 'UTF-8')
{
$sOutputData = UTF8_BOM.iconv('UTF-8', 'UTF-8//IGNORE//TRANSLIT', $sCSVData);
$sOutputData = UTF8_BOM.$sCSVData;
}
else
{
$sOutputData = iconv('UTF-8', $sCharset.'//IGNORE//TRANSLIT', $sCSVData);
$sOutputData = $sCSVData;
}
if ($sFileName == '')
{

View File

@@ -91,6 +91,7 @@ abstract class ModuleInstallerAPI
* Helper to modify an enum value
* The change is made in the datamodel definition, but the value has to be changed in the DB as well
* Must be called BEFORE DB update, i.e within an implementation of BeforeDatabaseCreation()
* This helper does change ONE value at a time
*
* @param string $sClass A valid class name
* @param string $sAttCode The enum attribute code
@@ -112,42 +113,73 @@ abstract class ModuleInstallerAPI
$aNewValues = array_keys($oValDef->GetValues(array(), ""));
if (in_array($sTo, $aNewValues))
{
$aAllValues = $aNewValues;
$aAllValues[] = $sFrom;
$sEnumCol = $oAttDef->Get("sql");
$aFields = CMDBSource::QueryToArray("SHOW COLUMNS FROM `$sTableName` WHERE Field = '$sEnumCol'");
if (isset($aFields[0]['Type']))
{
$sColType = $aFields[0]['Type'];
// Note: the parsing should rely on str_getcsv (requires PHP 5.3) to cope with escaped string
if (preg_match("/^enum\(\'(.*)\'\)$/", $sColType, $aMatches))
{
$aCurrentValues = explode("','", $aMatches[1]);
}
}
if (!in_array($sFrom, $aNewValues))
{
$sEnumCol = $oAttDef->Get("sql");
$sNullSpec = $oAttDef->IsNullAllowed() ? 'NULL' : 'NOT NULL';
if (strtolower($sTo) == strtolower($sFrom))
if (!in_array($sTo, $aCurrentValues)) // if not already transformed!
{
SetupPage::log_info("Changing enum in DB - $sClass::$sAttCode from '$sFrom' to '$sTo' (just a change in the case)");
$sColumnDefinition = "ENUM(".implode(",", CMDBSource::Quote($aNewValues)).") $sNullSpec";
$sRepair = "ALTER TABLE `$sTableName` MODIFY `$sEnumCol` $sColumnDefinition";
CMDBSource::Query($sRepair);
}
else
{
// 1st - Allow both values in the column definition
//
SetupPage::log_info("Changing enum in DB - $sClass::$sAttCode from '$sFrom' to '$sTo'");
$sColumnDefinition = "ENUM(".implode(",", CMDBSource::Quote($aAllValues)).") $sNullSpec";
$sRepair = "ALTER TABLE `$sTableName` MODIFY `$sEnumCol` $sColumnDefinition";
CMDBSource::Query($sRepair);
// 2nd - Change the old value into the new value
//
$sRepair = "UPDATE `$sTableName` SET `$sEnumCol` = '$sTo' WHERE `$sEnumCol` = BINARY '$sFrom'";
CMDBSource::Query($sRepair);
$iAffectedRows = CMDBSource::AffectedRows();
SetupPage::log_info("Changing enum in DB - $iAffectedRows rows updated");
// 3rd - Remove the useless value from the column definition
//
$sColumnDefinition = "ENUM(".implode(",", CMDBSource::Quote($aNewValues)).") $sNullSpec";
$sRepair = "ALTER TABLE `$sTableName` MODIFY `$sEnumCol` $sColumnDefinition";
CMDBSource::Query($sRepair);
SetupPage::log_info("Changing enum in DB - removed useless value '$sFrom'");
$sNullSpec = $oAttDef->IsNullAllowed() ? 'NULL' : 'NOT NULL';
if (strtolower($sTo) == strtolower($sFrom))
{
SetupPage::log_info("Changing enum in DB - $sClass::$sAttCode from '$sFrom' to '$sTo' (just a change in the case)");
$aTargetValues = array();
foreach ($aCurrentValues as $sValue)
{
if ($sValue == $sFrom)
{
$sValue = $sTo;
}
$aTargetValues[] = $sValue;
}
$sColumnDefinition = "ENUM(".implode(",", CMDBSource::Quote($aTargetValues)).") $sNullSpec";
$sRepair = "ALTER TABLE `$sTableName` MODIFY `$sEnumCol` $sColumnDefinition";
CMDBSource::Query($sRepair);
}
else
{
// 1st - Allow both values in the column definition
//
SetupPage::log_info("Changing enum in DB - $sClass::$sAttCode from '$sFrom' to '$sTo'");
$aAllValues = $aCurrentValues;
$aAllValues[] = $sTo;
$sColumnDefinition = "ENUM(".implode(",", CMDBSource::Quote($aAllValues)).") $sNullSpec";
$sRepair = "ALTER TABLE `$sTableName` MODIFY `$sEnumCol` $sColumnDefinition";
CMDBSource::Query($sRepair);
// 2nd - Change the old value into the new value
//
$sRepair = "UPDATE `$sTableName` SET `$sEnumCol` = '$sTo' WHERE `$sEnumCol` = BINARY '$sFrom'";
CMDBSource::Query($sRepair);
$iAffectedRows = CMDBSource::AffectedRows();
SetupPage::log_info("Changing enum in DB - $iAffectedRows rows updated");
// 3rd - Remove the useless value from the column definition
//
$aTargetValues = array();
foreach ($aCurrentValues as $sValue)
{
if ($sValue == $sFrom)
{
$sValue = $sTo;
}
$aTargetValues[] = $sValue;
}
$sColumnDefinition = "ENUM(".implode(",", CMDBSource::Quote($aTargetValues)).") $sNullSpec";
$sRepair = "ALTER TABLE `$sTableName` MODIFY `$sEnumCol` $sColumnDefinition";
CMDBSource::Query($sRepair);
SetupPage::log_info("Changing enum in DB - removed useless value '$sFrom'");
}
}
}
else

View File

@@ -223,15 +223,15 @@ if (!empty($sExpression))
case 'csv':
$oP = new CSVPage("iTop - Export");
$sFields = implode(',', $aFields);
$sCSVData = cmdbAbstractObject::GetSetAsCSV($oSet, array('fields' => $sFields, 'fields_advanced' => $bFieldsAdvanced, 'localize_values' => $bLocalize));
$sCharset = MetaModel::GetConfig()->Get('csv_file_default_charset');
$sCharset = utils::ReadParam('charset', MetaModel::GetConfig()->Get('csv_file_default_charset'), true /* Allow CLI */, 'raw_data');
$sCSVData = cmdbAbstractObject::GetSetAsCSV($oSet, array('fields' => $sFields, 'fields_advanced' => $bFieldsAdvanced, 'localize_values' => $bLocalize), $sCharset);
if ($sCharset == 'UTF-8')
{
$sOutputData = UTF8_BOM.iconv('UTF-8', 'UTF-8//IGNORE//TRANSLIT', $sCSVData);
$sOutputData = UTF8_BOM.$sCSVData;
}
else
{
$sOutputData = iconv('UTF-8', $sCharset.'//IGNORE//TRANSLIT', $sCSVData);
$sOutputData = $sCSVData;
}
if ($sFileName == '')
{