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); 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 $sSeparator = isset($aParams['separator']) ? $aParams['separator'] : ','; // default separator is comma
$sTextQualifier = isset($aParams['text_qualifier']) ? $aParams['text_qualifier'] : '"'; // default text qualifier is double quote $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 else
{ {
$value = $oObj->Get($sAttCodeEx); $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); $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 = new DBObjectSearch($sTargetClass);
$oSearch->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', true); $oSearch->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', true);
$oAllowedValues = new DBObjectSet($oSearch); $oAllowedValues = new DBObjectSet($oSearch);
$iFieldSize = $oAttDef->GetMaxSize(); $iFieldSize = $oKeyAttDef->GetMaxSize();
$iMaxComboLength = $oAttDef->GetMaximumComboLength(); $iMaxComboLength = $oKeyAttDef->GetMaximumComboLength();
$sHtml .= "<label>".MetaModel::GetFilterLabel($sClassName, $sFilterCode).":</label>&nbsp;"; $sHtml .= "<label>".MetaModel::GetFilterLabel($sKeyAttClass, $sKeyAttCode).":</label>&nbsp;";
$aExtKeyParams = $aExtraParams; $aExtKeyParams = $aExtraParams;
$aExtKeyParams['iFieldSize'] = $oAttDef->GetMaxSize(); $aExtKeyParams['iFieldSize'] = $oKeyAttDef->GetMaxSize();
$aExtKeyParams['iMinChars'] = $oAttDef->GetMinAutoCompleteChars(); $aExtKeyParams['iMinChars'] = $oKeyAttDef->GetMinAutoCompleteChars();
$sHtml .= UIExtKeyWidget::DisplayFromAttCode($oPage, $sFilterCode, $sClassName, $oAttDef->GetLabel(), $oAllowedValues, $sFilterValue, $sSearchFormId.'search_'.$sFilterCode, false, $sFilterCode, '', $aExtKeyParams, true); $sHtml .= UIExtKeyWidget::DisplayFromAttCode($oPage, $sKeyAttCode, $sKeyAttClass, $oAttDef->GetLabel(), $oAllowedValues, $sFilterValue, $sSearchFormId.'search_'.$sFilterCode, false, $sFilterCode, '', $aExtKeyParams, true);
} }
else else
{ {
@@ -3074,7 +3079,7 @@ EOF
// Attribute is read-only // Attribute is read-only
$sHTMLValue = $this->GetAsHTML($sAttCode); $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; $aFieldsMap[$sAttCode] = $sInputId;
$sComment .= $sSynchroIcon; $sComment .= $sSynchroIcon;
} }
@@ -3251,7 +3256,11 @@ EOF
$currValue = $aKeys[0]; // The only value is the first key $currValue = $aKeys[0]; // The only value is the first key
//echo "<p>current value for $sAttCode : $currValue</p>"; //echo "<p>current value for $sAttCode : $currValue</p>";
$oDummyObj->Set($sAttCode, $currValue); $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>'; $aComments[$sAttCode] .= '<div class="mono_value">1</div>';
} }
else 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' }} } );"; $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); $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>'; $aComments[$sAttCode] .= '<div class="multi_values" id="multi_values_'.$sAttCode.'">'.$iCount.'</div>';
} }
$sReadyScript .= 'ToogleField('.(($iCount == 1) ? 'true': 'false').', \''.$iFormId.'_'.$sAttCode.'\');'."\n"; $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)) 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->sLayoutClass = $aParams['layout_class'];
$this->sTitle = $aParams['title']; $this->sTitle = $aParams['title'];
$this->bAutoReload = $aParams['auto_reload'] == 'true'; $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) foreach($aParams['cells'] as $aCell)
{ {
@@ -300,7 +300,7 @@ abstract class Dashboard
public function SetAutoReloadInterval($iAutoReloadSec) 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) public function AddDashlet($oDashlet)
@@ -312,7 +312,7 @@ abstract class Dashboard
public function Render($oPage, $bEditMode = false, $aExtraParams = array()) 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 = new $this->sLayoutClass;
$oLayout->Render($oPage, $this->aCells, $bEditMode, $aExtraParams); $oLayout->Render($oPage, $this->aCells, $bEditMode, $aExtraParams);
if (!$bEditMode) if (!$bEditMode)
@@ -357,16 +357,17 @@ abstract class Dashboard
$oField = new DesignerBooleanField('auto_reload', Dict::S('UI:DashboardEdit:AutoReload'), $this->bAutoReload); $oField = new DesignerBooleanField('auto_reload', Dict::S('UI:DashboardEdit:AutoReload'), $this->bAutoReload);
$oForm->AddField($oField); $oForm->AddField($oField);
$oField = new DesignerTextField('auto_reload_sec', Dict::S('UI:DashboardEdit:AutoReloadSec'), $this->iAutoReloadSec); $oField = new DesignerIntegerField('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->SetBoundaries(MetaModel::GetConfig()->Get('min_reload_interval'), null); // no upper limit
$oForm->AddField($oField); $oForm->AddField($oField);
$this->SetFormParams($oForm); $this->SetFormParams($oForm);
$oForm->RenderAsPropertySheet($oPage, false, '.itop-dashboard'); $oForm->RenderAsPropertySheet($oPage, false, '.itop-dashboard');
$oPage->add('</div>'); $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( $oPage->add_ready_script(
<<<EOF <<<EOF
// Note: the title gets deleted by the validation mechanism // 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']; $sGroupBy = $this->aProperties['group_by'];
$sStyle = $this->aProperties['style']; $sStyle = $this->aProperties['style'];
// First perform the query - if the OQL is not ok, it will generate an exception : no need to go further // 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); try
$sClass = $oQuery->GetClass(); {
$sClassAlias = $oQuery->GetClassAlias(); $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 // Check groupby... it can be wrong at this stage
if (preg_match('/^(.*):(.*)$/', $sGroupBy, $aMatches)) if (preg_match('/^(.*):(.*)$/', $sGroupBy, $aMatches))
{ {

View File

@@ -551,6 +551,7 @@ EOF;
$oPage->add_ready_script( $oPage->add_ready_script(
<<<EOF <<<EOF
var oTable = $('#{$this->iListId} table.listResults'); 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}); 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 EOF
); );

View File

@@ -226,7 +226,7 @@ class DisplayBlock
if (is_numeric($aExtraParams['auto_reload']) && ($aExtraParams['auto_reload'] > 0)) if (is_numeric($aExtraParams['auto_reload']) && ($aExtraParams['auto_reload'] > 0))
{ {
$bAutoReload = true; $bAutoReload = true;
$iReloadInterval = max(5, $aExtraParams['auto_reload'])*1000; $iReloadInterval = max(MetaModel::GetConfig()->Get('min_reload_interval'), $aExtraParams['auto_reload'])*1000;
} }
else else
{ {
@@ -783,7 +783,8 @@ class DisplayBlock
$sLinkToToggle = $sLinkToToggle.'&advanced=1'; $sLinkToToggle = $sLinkToToggle.'&advanced=1';
$sChecked = ''; $sChecked = '';
} }
$sAjaxLink = $sDownloadLink.'&charset=UTF-8'; // Includes &fields_advanced=1 if in advanced mode
/* /*
$sCSVData = cmdbAbstractObject::GetSetAsCSV($this->m_oSet, array('fields_advanced' => $bAdvancedMode)); $sCSVData = cmdbAbstractObject::GetSetAsCSV($this->m_oSet, array('fields_advanced' => $bAdvancedMode));
$sCharset = MetaModel::GetConfig()->Get('csv_file_default_charset'); $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 .= "<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 .= htmlentities($sCSVData, ENT_QUOTES, 'UTF-8');
$sHtml .= "</textarea>\n"; $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; break;
case 'modify': 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 class DesignerComboField extends DesignerFormField
{ {
protected $aAllowedValues; protected $aAllowedValues;

View File

@@ -305,16 +305,20 @@ class LoginWebPage extends NiceWebPage
{ {
$this->add("<p>".Dict::Format('UI:ResetPwd-Error-WrongLogin', $sAuthUser)."</p>\n"); $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 else
{ {
$this->add("<p>".Dict::Format('UI:ResetPwd-Error-EnterPassword', $oUser->GetFriendlyName())."</p>\n"); $oEncryptedToken = $oUser->Get('reset_pwd_token');
$sInconsistenPwdMsg = Dict::S('UI:Login:RetypePwdDoesNotMatch'); if (!$oEncryptedToken->CheckPassword($sToken))
$this->add_script( {
$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 <<<EOF
function DoCheckPwd() function DoCheckPwd()
{ {
@@ -326,18 +330,19 @@ function DoCheckPwd()
return true; return true;
} }
EOF EOF
); );
$this->add("<form method=\"post\">\n"); $this->add("<form method=\"post\">\n");
$this->add("<table>\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=\"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 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("<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("</table>\n");
$this->add("<input type=\"hidden\" name=\"loginop\" value=\"do_reset_pwd\" />\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=\"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("<input type=\"hidden\" name=\"token\" value=\"".htmlentities($sToken, ENT_QUOTES, 'UTF-8')."\" />\n");
$this->add("</form>\n"); $this->add("</form>\n");
$this->add("</div\n"); $this->add("</div\n");
}
} }
} }
@@ -357,21 +362,25 @@ EOF
{ {
$this->add("<p>".Dict::Format('UI:ResetPwd-Error-WrongLogin', $sAuthUser)."</p>\n"); $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 else
{ {
// Trash the token and change the password $oEncryptedPassword = $oUser->Get('reset_pwd_token');
$oUser->Set('reset_pwd_token', ''); if (!$oEncryptedPassword->CheckPassword($sToken))
$oUser->SetPassword($sNewPwd); // Does record the change into the DB {
$this->add("<p>".Dict::S('UI:ResetPwd-Error-InvalidToken')."</p>\n");
$this->add("<p>".Dict::S('UI:ResetPwd-Ready')."</p>"); }
$sUrl = utils::GetAbsoluteUrlAppRoot(); else
$this->add("<p><a href=\"$sUrl\">".Dict::S('UI:ResetPwd-Login')."</a></p>"); {
// 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) 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); $oField = new DesignerBooleanField('auto_reload', Dict::S('Class:ShortcutOQL/Attribute:auto_reload'), false);
$oForm->AddField($oField); $oForm->AddField($oField);
$oField = new DesignerTextField('auto_reload_sec', Dict::S('Class:ShortcutOQL/Attribute:auto_reload_sec'), MetaModel::GetConfig()->GetStandardReloadInterval()); $oField = new DesignerIntegerField('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->SetBoundaries(MetaModel::GetConfig()->Get('min_reload_interval'), null); // no upper limit
$oField->SetMandatory(false); $oField->SetMandatory(false);
$oForm->AddField($oField); $oForm->AddField($oField);
@@ -284,7 +284,7 @@ class ShortcutOQL extends Shortcut
$oAppContext = new ApplicationContext(); $oAppContext = new ApplicationContext();
$sContext = $oAppContext->GetForLink(); $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( $oPage->add_ready_script(
<<<EOF <<<EOF

View File

@@ -28,10 +28,11 @@ require_once(APPROOT.'/core/cmdbobject.class.inc.php');
require_once(APPROOT.'/application/utils.inc.php'); require_once(APPROOT.'/application/utils.inc.php');
session_name('itop-'.md5(APPROOT)); session_name('itop-'.md5(APPROOT));
session_start(); 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'] = $sSwitchEnv;
$_SESSION['itop_env'] = $sEnv; $sEnv = $sSwitchEnv;
// TODO: reset the credentials as well ?? // TODO: reset the credentials as well ??
} }
else if (isset($_SESSION['itop_env'])) else if (isset($_SESSION['itop_env']))

View File

@@ -239,7 +239,7 @@ EOF
*/ */
protected function DisplayFormTable(WebPage $oP, $aConfig, $aData) 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"; $sHtml .= "<table class=\"listResults\">\n";
// Header // Header
$sHtml .= "<thead>\n"; $sHtml .= "<thead>\n";
@@ -259,11 +259,11 @@ EOF
$sEmptyRowStyle = 'style="display:none;"'; $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) foreach($aData as $iRowId => $aRow)
{ {
$sHtml .= $this->DisplayFormRow($oP, $aConfig, $aRow, $iRowId); $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"; $sHtml .= "</tbody>\n";
// Footer // 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 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("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 try
{ {
$this->Set('status', 'running'); if ($this->Get('status') == 'running')
$this->Set('started', time()); {
$this->DBUpdate(); return self::ALREADY_RUNNING;
return self::OK; }
else
{
$this->Set('status', 'running');
$this->Set('started', time());
$this->DBUpdate();
return self::OK;
}
} }
catch(Exception $e) catch(Exception $e)
{ {
// Corrupted task !! (for example: "Failed to reload object") // 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.'); IssueLog::Error('Failed to process async task #'.$this->GetKey().' - reason: '.$e->getMessage().' - fatal error, deleting the task.');
if ($this->Get('event_id') != 0) if ($this->Get('event_id') != 0)
{ {
$oEventLog = MetaModel::GetObject('Event', $this->Get('event_id')); $oEventLog = MetaModel::GetObject('Event', $this->Get('event_id'));
$oEventLog->Set('message', 'Failed, corrupted data: '.$e->getMessage()); $oEventLog->Set('message', 'Failed, corrupted data: '.$e->getMessage());
$oEventLog->DBUpdate(); $oEventLog->DBUpdate();
} }
$this->DBDelete(); $this->DBDelete();
return self::DELETED; return self::DELETED;
} }
} }
public function GetRetryDelay() public function GetRetryDelay($iErrorCode = null)
{ {
$iRetryDelay = 600; $iRetryDelay = 600;
$aRetries = MetaModel::GetConfig()->Get('async_task_retries', array()); $aRetries = MetaModel::GetConfig()->Get('async_task_retries', array());
@@ -149,7 +159,7 @@ abstract class AsyncTask extends DBObject
return $iRetryDelay; return $iRetryDelay;
} }
public function GetMaxRetries() public function GetMaxRetries($iErrorCode = null)
{ {
$iMaxRetries = 0; $iMaxRetries = 0;
$aRetries = MetaModel::GetConfig()->Get('async_task_retries', array()); $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() protected function OnInsert()
{ {
$this->Set('created', time()); $this->Set('created', time());
$this->Set('remaining_retries', $this->GetMaxRetries());
} }
/** /**
@@ -191,22 +207,7 @@ abstract class AsyncTask extends DBObject
} }
catch(Exception $e) catch(Exception $e)
{ {
$iRemaining = $this->Get('remaining_retries'); $this->HandleError($e->getMessage(), $e->getCode());
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());
}
} }
} }
else else
@@ -217,7 +218,56 @@ abstract class AsyncTask extends DBObject
return $bRet; 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(); 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; break;
default: 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); $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) public function DBInsertTracked(CMDBChange $oChange, $bSkipStrongSecurity = null)
{ {
self::SetCurrentChange($oChange); self::SetCurrentChange($oChange);
$this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_MODIFY); $this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_MODIFY);
$ret = $this->DBInsertTracked_Internal(); $ret = $this->DBInsertTracked_Internal();
return $ret; return $ret;
} }
public function DBInsertTrackedNoReload(CMDBChange $oChange, $bSkipStrongSecurity = null) public function DBInsertTrackedNoReload(CMDBChange $oChange, $bSkipStrongSecurity = null)
{ {
self::SetCurrentChange($oChange); self::SetCurrentChange($oChange);
$this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_MODIFY); $this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_MODIFY);
$ret = $this->DBInsertTracked_Internal(true); $ret = $this->DBInsertTracked_Internal(true);
return $ret; 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) protected function DBInsertTracked_Internal($bDoNotReload = false)
{ {
if ($bDoNotReload) if ($bDoNotReload)
{ {
$ret = parent::DBInsertNoReload(); $ret = $this->DBInsertNoReload();
} }
else else
{ {
$ret = parent::DBInsert(); $ret = $this->DBInsert();
} }
return $ret; return $ret;
} }

View File

@@ -79,7 +79,7 @@ class CMDBSource
// Override the default port // Override the default port
$sServer = $aConnectInfo[0]; $sServer = $aConnectInfo[0];
$iPort = (int)$aConnectInfo[1]; $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 else
{ {

View File

@@ -769,6 +769,14 @@ class Config
'source_of_value' => '', 'source_of_value' => '',
'show_in_conf_sample' => false, '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) public function IsProperty($sPropCode)

View File

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

View File

@@ -351,7 +351,7 @@ class EventLoginUsage extends Event
MetaModel::Init_Params($aParams); MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes(); 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'); $aZList = array('date', 'user_id');
if (MetaModel::IsValidAttCode('Contact', 'name')) 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; $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; return $aResult;
} }

View File

@@ -835,6 +835,10 @@ abstract class MetaModel
final static public function GetAttributeDef($sClass, $sAttCode) final static public function GetAttributeDef($sClass, $sAttCode)
{ {
self::_check_subclass($sClass); 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]; 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 : ''; $sText = isset($oJson->message) ? $oJson->message : '';
@@ -394,16 +394,24 @@ class ormCaseLog {
{ {
throw new Exception("Only administrators can set the user id", RestResult::UNAUTHORIZED); 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 else
{ {

View File

@@ -371,7 +371,7 @@ abstract class UserInternal extends User
MetaModel::Init_InheritAttributes(); MetaModel::Init_InheritAttributes();
// When set, this token allows for password reset // 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 // 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 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('<span style="display:none;" id="attachment_loading">Loading, please wait...</span>');
$oPage->p('<input type="hidden" id="attachment_plugin" name="attachment_plugin"/>'); $oPage->p('<input type="hidden" id="attachment_plugin" name="attachment_plugin"/>');
$oPage->add('</fieldset>');
if ($this->m_bDeleteEnabled) if ($this->m_bDeleteEnabled)
{ {
$oPage->add_ready_script('$(".attachment").hover( function() {$(this).children(":button").toggleClass("btn_hidden"); } );'); $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('<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')); $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'; }}});"); $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:DashboardTitle' => 'Title',
'UI:DashboardEdit:AutoReload' => 'Automatic refresh', 'UI:DashboardEdit:AutoReload' => 'Automatic refresh',
'UI:DashboardEdit:AutoReloadSec' => 'Automatic refresh interval (seconds)', '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:Layout' => 'Layout',
'UI:DashboardEdit:Properties' => 'Dashboard Properties', '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:none' => 'Disabled',
'Class:ShortcutOQL/Attribute:auto_reload/Value:custom' => 'Custom rate', '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' => '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.', '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:DashboardTitle' => 'Título',
'UI:DashboardEdit:AutoReload' => 'Actualización Automática', 'UI:DashboardEdit:AutoReload' => 'Actualización Automática',
'UI:DashboardEdit:AutoReloadSec' => 'Interválo de Actualización Automática (segundos)', '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:Layout' => 'Distribución',
'UI:DashboardEdit:Properties' => 'Propiedades', 'UI:DashboardEdit:Properties' => 'Propiedades',
'UI:DashboardEdit:Dashlets' => 'Dashlets disponibles', '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:none' => 'Deshabilitado',
'Class:ShortcutOQL/Attribute:auto_reload/Value:custom' => 'Frecuencia configurable', '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' => '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:FillAllMandatoryFields' => 'Por favor llenar los campos obligatorios.',
'UI:CSVImportConfirmTitle' => 'Por favor confirme la operación', 'UI:CSVImportConfirmTitle' => 'Por favor confirme la operación',
'UI:CSVImportConfirmMessage' => '¿Está seguro?', '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:DashboardTitle' => 'Titre',
'UI:DashboardEdit:AutoReload' => 'Réactualisation automatique', 'UI:DashboardEdit:AutoReload' => 'Réactualisation automatique',
'UI:DashboardEdit:AutoReloadSec' => 'Réactualisation toutes les (secondes)', '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:Layout' => 'Mise en page',
'UI:DashboardEdit:Properties' => 'Propriétés du tableau de bord', 'UI:DashboardEdit:Properties' => 'Propriétés du tableau de bord',
'UI:DashboardEdit:Dashlets' => 'Indicateurs', '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:none' => 'Désactivée',
'Class:ShortcutOQL/Attribute:auto_reload/Value:custom' => 'Personnalisé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' => '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.', '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:DashboardTitle' => 'Título',
'UI:DashboardEdit:AutoReload' => 'Atualizar automaticamente', 'UI:DashboardEdit:AutoReload' => 'Atualizar automaticamente',
'UI:DashboardEdit:AutoReloadSec' => 'Intervalo atualização automática (segundos)', '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:Layout' => 'Layout',
'UI:DashboardEdit:Properties' => 'Propriedades', '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:none' => 'Desabilitado',
'Class:ShortcutOQL/Attribute:auto_reload/Value:custom' => 'Avaliar', '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' => '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:FillAllMandatoryFields' => 'Por favor, preencha todos os campos obrigatórios.',
'UI:CSVImportConfirmTitle' => 'Por favor, confirme a operação', '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:DashboardTitle' => 'Заголовок',
'UI:DashboardEdit:AutoReload' => 'Обновлять автоматически', 'UI:DashboardEdit:AutoReload' => 'Обновлять автоматически',
'UI:DashboardEdit:AutoReloadSec' => 'Интервал обновления (секунды)', 'UI:DashboardEdit:AutoReloadSec' => 'Интервал обновления (секунды)',
'UI:DashboardEdit:AutoReloadSec+' => 'Минимальный интервал 5 секунд', 'UI:DashboardEdit:AutoReloadSec+' => 'Минимальный интервал %1$d секунд',
'UI:DashboardEdit:Layout' => 'Макет', 'UI:DashboardEdit:Layout' => 'Макет',
'UI:DashboardEdit:Properties' => 'Свойства дашборда', '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:none' => 'Disabled',
'Class:ShortcutOQL/Attribute:auto_reload/Value:custom' => 'Custom rate', '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' => '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' => 'Пожалуйста, заполните все обязательные поля.', 'UI:FillAllMandatoryFields' => 'Пожалуйста, заполните все обязательные поля.',

View File

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

View File

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

View File

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

View File

@@ -91,6 +91,7 @@ abstract class ModuleInstallerAPI
* Helper to modify an enum value * 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 * 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() * 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 $sClass A valid class name
* @param string $sAttCode The enum attribute code * @param string $sAttCode The enum attribute code
@@ -112,42 +113,73 @@ abstract class ModuleInstallerAPI
$aNewValues = array_keys($oValDef->GetValues(array(), "")); $aNewValues = array_keys($oValDef->GetValues(array(), ""));
if (in_array($sTo, $aNewValues)) if (in_array($sTo, $aNewValues))
{ {
$aAllValues = $aNewValues; $sEnumCol = $oAttDef->Get("sql");
$aAllValues[] = $sFrom; $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)) if (!in_array($sFrom, $aNewValues))
{ {
$sEnumCol = $oAttDef->Get("sql"); if (!in_array($sTo, $aCurrentValues)) // if not already transformed!
$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)"); $sNullSpec = $oAttDef->IsNullAllowed() ? 'NULL' : 'NOT NULL';
$sColumnDefinition = "ENUM(".implode(",", CMDBSource::Quote($aNewValues)).") $sNullSpec";
$sRepair = "ALTER TABLE `$sTableName` MODIFY `$sEnumCol` $sColumnDefinition"; if (strtolower($sTo) == strtolower($sFrom))
CMDBSource::Query($sRepair); {
} SetupPage::log_info("Changing enum in DB - $sClass::$sAttCode from '$sFrom' to '$sTo' (just a change in the case)");
else $aTargetValues = array();
{ foreach ($aCurrentValues as $sValue)
// 1st - Allow both values in the column definition {
// if ($sValue == $sFrom)
SetupPage::log_info("Changing enum in DB - $sClass::$sAttCode from '$sFrom' to '$sTo'"); {
$sColumnDefinition = "ENUM(".implode(",", CMDBSource::Quote($aAllValues)).") $sNullSpec"; $sValue = $sTo;
$sRepair = "ALTER TABLE `$sTableName` MODIFY `$sEnumCol` $sColumnDefinition"; }
CMDBSource::Query($sRepair); $aTargetValues[] = $sValue;
}
// 2nd - Change the old value into the new value $sColumnDefinition = "ENUM(".implode(",", CMDBSource::Quote($aTargetValues)).") $sNullSpec";
// $sRepair = "ALTER TABLE `$sTableName` MODIFY `$sEnumCol` $sColumnDefinition";
$sRepair = "UPDATE `$sTableName` SET `$sEnumCol` = '$sTo' WHERE `$sEnumCol` = BINARY '$sFrom'"; CMDBSource::Query($sRepair);
CMDBSource::Query($sRepair); }
$iAffectedRows = CMDBSource::AffectedRows(); else
SetupPage::log_info("Changing enum in DB - $iAffectedRows rows updated"); {
// 1st - Allow both values in the column definition
// 3rd - Remove the useless value from the column definition //
// SetupPage::log_info("Changing enum in DB - $sClass::$sAttCode from '$sFrom' to '$sTo'");
$sColumnDefinition = "ENUM(".implode(",", CMDBSource::Quote($aNewValues)).") $sNullSpec"; $aAllValues = $aCurrentValues;
$sRepair = "ALTER TABLE `$sTableName` MODIFY `$sEnumCol` $sColumnDefinition"; $aAllValues[] = $sTo;
CMDBSource::Query($sRepair); $sColumnDefinition = "ENUM(".implode(",", CMDBSource::Quote($aAllValues)).") $sNullSpec";
SetupPage::log_info("Changing enum in DB - removed useless value '$sFrom'"); $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 else

View File

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