diff --git a/application/dashboard.class.inc.php b/application/dashboard.class.inc.php
index 4970f6c38..a97843c51 100644
--- a/application/dashboard.class.inc.php
+++ b/application/dashboard.class.inc.php
@@ -203,7 +203,7 @@ abstract class Dashboard
$oField = new DesignerLongTextField('dashboard_title', Dict::S('UI:DashboardEdit:DashboardTitle'), $this->sTitle);
$oForm->AddField($oField);
$this->SetFormParams($oForm);
- $oForm->RenderAsPropertySheet($oPage);
+ $oForm->RenderAsPropertySheet($oPage, false, ':itop-dashboard');
$oPage->add('');
$oPage->add_ready_script(
@@ -213,7 +213,7 @@ abstract class Dashboard
var sLayoutClass = $(this).val();
$(':itop-dashboard').dashboard('option', {layout_class: sLayoutClass});
} );
- $('#row_attr_dashboard_title').property_field('option', {'do_apply': function() {
+ $('#row_attr_dashboard_title').property_field('option', {parent_selector: ':itop-dashboard', auto_apply: false, 'do_apply': function() {
var sTitle = $('#attr_dashboard_title').val();
$(':itop-dashboard').dashboard('option', {title: sTitle});
return true;
@@ -272,7 +272,7 @@ EOF
$oPage->add('
');
$oForm = $oDashlet->GetForm();
$this->SetFormParams($oForm);
- $oForm->RenderAsPropertySheet($oPage);
+ $oForm->RenderAsPropertySheet($oPage, false, ':itop-dashboard');
$oPage->add('
');
}
}
@@ -430,9 +430,15 @@ EOF
$sLayoutClass = addslashes($this->sLayoutClass);
$sTitle = addslashes($this->sTitle);
$sUrl = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php';
+
+ $sExitConfirmationMessage = addslashes(Dict::S('UI:NavigateAwayConfirmationMessage'));
+ $sCancelConfirmationMessage = addslashes(Dict::S('UI:CancelConfirmationMessage'));
+ $sAutoApplyConfirmationMessage = addslashes(Dict::S('UI:AutoApplyConfirmationMessage'));
$oPage->add_ready_script(
<<add_ready_script("");
diff --git a/application/dashlet.class.inc.php b/application/dashlet.class.inc.php
index ee43515ee..5913d3c7c 100644
--- a/application/dashlet.class.inc.php
+++ b/application/dashlet.class.inc.php
@@ -938,13 +938,13 @@ class DashletHeaderDynamic extends Dashlet
catch(Exception $e)
{
$oField = new DesignerTextField('group_by', Dict::S('UI:DashletHeaderDynamic:Prop-GroupBy'), $this->aProperties['group_by']);
- $oField->SetMandatory();
+ $oField->SetReadOnly();
}
$oForm->AddField($oField);
$oField = new DesignerComboField('values', Dict::S('UI:DashletHeaderDynamic:Prop-Values'), $this->aProperties['values']);
$oField->MultipleSelection(true);
- if (MetaModel::IsValidAttCode($sClass, $this->aProperties['group_by']))
+ if (isset($sClass) && MetaModel::IsValidAttCode($sClass, $this->aProperties['group_by']))
{
$aValues = MetaModel::GetAllowedValues_att($sClass, $this->aProperties['group_by']);
$oField->SetAllowedValues($aValues);
diff --git a/application/forms.class.inc.php b/application/forms.class.inc.php
index d70c5432a..e621610e7 100644
--- a/application/forms.class.inc.php
+++ b/application/forms.class.inc.php
@@ -144,7 +144,7 @@ class DesignerForm
}
- public function RenderAsPropertySheet($oP, $bReturnHTML = false)
+ public function RenderAsPropertySheet($oP, $bReturnHTML = false, $sNotifyParentSelector = null)
{
$sReturn = '';
$sActionUrl = addslashes($this->sSubmitTo);
@@ -183,9 +183,11 @@ class DesignerForm
{
$sReturn .= $sValidationFields;
}
+ $sNotifyParentSelectorJS = is_null($sNotifyParentSelector) ? 'null' : "'".addslashes($sNotifyParentSelector)."'";
+ $sAutoApply = $oField->IsAutoApply() ? 'true' : 'false';
$this->AddReadyScript(
<<defaultValue = $defaultValue;
$this->bMandatory = false;
$this->bReadOnly = false;
+ $this->bAutoApply = false;
}
public function GetCode()
@@ -517,7 +521,17 @@ class DesignerFormField
{
return ($this->oForm->IsReadOnly() || $this->bReadOnly);
}
-
+
+ public function SetAutoApply($bAutoApply)
+ {
+ $this->bAutoApply = $bAutoApply;
+ }
+
+ public function IsAutoApply()
+ {
+ return $this->bAutoApply;
+ }
+
public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
{
$sId = $this->oForm->GetFieldId($this->sCode);
@@ -663,6 +677,8 @@ class DesignerComboField extends DesignerFormField
$this->aAllowedValues = array();
$this->bMultipleSelection = false;
$this->bOtherChoices = false;
+
+ $this->bAutoApply = true;
}
public function SetAllowedValues($aAllowedValues)
@@ -740,6 +756,7 @@ class DesignerBooleanField extends DesignerFormField
public function __construct($sCode, $sLabel = '', $defaultValue = '')
{
parent::__construct($sCode, $sLabel, $defaultValue);
+ $this->bAutoApply = true;
}
public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
@@ -812,6 +829,7 @@ class DesignerIconSelectionField extends DesignerFormField
public function __construct($sCode, $sLabel = '', $defaultValue = '')
{
parent::__construct($sCode, $sLabel, $defaultValue);
+ $this->bAutoApply = true;
}
public function SetAllowedValues($aAllowedValues)
diff --git a/css/light-grey.css b/css/light-grey.css
index 38e00e3b2..982f2192c 100644
--- a/css/light-grey.css
+++ b/css/light-grey.css
@@ -1202,8 +1202,8 @@ td.prop_value {
tr.itop-property-field-modified td {
background: #fbb;
}
-tr.itop-property-field-modified td.prop_value.hover {
- background: #fbb;
+tr.itop-property-field-modified td.hover {
+ background: #f99;
}
td.prop_value textarea, td.prop_value input[type=text]{
width: 98%;
diff --git a/dictionaries/dictionary.itop.ui.php b/dictionaries/dictionary.itop.ui.php
index 6aaeb32c9..a5d70bf00 100644
--- a/dictionaries/dictionary.itop.ui.php
+++ b/dictionaries/dictionary.itop.ui.php
@@ -960,6 +960,8 @@ When associated with a trigger, each action is given an "order" number, specifyi
'UI:FavoriteOtherSettings' => 'Other Settings',
'UI:Favorites:Default_X_ItemsPerPage' => 'Default length for lists: %1$s items per page',
'UI:NavigateAwayConfirmationMessage' => 'Any modification will be discarded.',
+ 'UI:CancelConfirmationMessage' => 'You will loose your changes. Continue anyway?',
+ 'UI:AutoApplyConfirmationMessage' => 'Some changes have not been applied yet. Do you want itop to take them into account?',
'UI:Create_Class_InState' => 'Create the %1$s in state: ',
'UI:OrderByHint_Values' => 'Sort order: %1$s',
'UI:Menu:AddToDashboard' => 'Add To Dashboard...',
diff --git a/dictionaries/fr.dictionary.itop.ui.php b/dictionaries/fr.dictionary.itop.ui.php
index 2d65bcd50..d69dafb2e 100644
--- a/dictionaries/fr.dictionary.itop.ui.php
+++ b/dictionaries/fr.dictionary.itop.ui.php
@@ -804,6 +804,8 @@ Lors de l\'association à un déclencheur, on attribue à chaque action un numé
'UI:FavoriteOtherSettings' => 'Autres réglages',
'UI:Favorites:Default_X_ItemsPerPage' => 'Longueur par défaut des listes: %1$s éléments par page',
'UI:NavigateAwayConfirmationMessage' => 'Toute modification sera perdue.',
+ 'UI:CancelConfirmationMessage' => 'Vous allez perdre vos modifications. Voulez-vous continuer ?',
+ 'UI:AutoApplyConfirmationMessage' => 'Des modifications n\'ont pas encore été prises en compte. Voulez-vous qu\'elles soient prises en compte automatiquement ?',
'UI:Create_Class_InState' => 'Créer l\'objet %1$s dans l\'état: ',
'UI:OrderByHint_Values' => 'Ordre de tri: %1$s',
'UI:Menu:AddToDashboard' => 'Ajouter au Tableau de Bord...',
diff --git a/js/dashboard.js b/js/dashboard.js
index 2f353c5a6..a8581c12a 100644
--- a/js/dashboard.js
+++ b/js/dashboard.js
@@ -24,10 +24,13 @@ $(function()
var me = this;
this.element
- .addClass('itop-dashboard');
+ .addClass('itop-dashboard')
+ .bind('mark_as_modified.itop-dashboard', function(){me.mark_as_modified();} );
this.ajax_div = $('').appendTo(this.element);
this._make_draggable();
+ this.bModified = false;
+
},
// called when created, and later when changing options
@@ -96,6 +99,32 @@ $(function()
return oState;
},
+ // Modified means: at least one change has been applied
+ mark_as_modified: function()
+ {
+ this.bModified = true;
+ },
+ is_modified: function()
+ {
+ return this.bModified;
+ },
+ // Dirty means: at least one change has not been committed yet
+ is_dirty: function()
+ {
+ if ($('#dashboard_editor .ui-layout-east .itop-property-field-modified').size() > 0)
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ },
+ // Force the changes of all the properties being "dirty"
+ apply_changes: function()
+ {
+ $('#dashboard_editor .ui-layout-east .itop-property-field-modified').trigger('apply_changes');
+ },
save: function()
{
var oParams = this._get_state(this.options.submit_parameters);
diff --git a/js/dashlet.js b/js/dashlet.js
index 1e8691e87..7e3c06261 100644
--- a/js/dashlet.js
+++ b/js/dashlet.js
@@ -20,11 +20,30 @@ $(function()
this.element
.addClass('itop-dashlet')
.bind('click.itop-dashlet', function(event) { me._on_click(event); } );
-
- this.closeBox = $('');
- this.closeBox.click(function() { me._remove_dashlet(); }).hide().prependTo(this.element);
+
+ this._update();
},
+ // to call when the contents are changed
+ _update: function()
+ {
+ var me = this;
+
+ this.closeBox = $('');
+ this.closeBox
+ .click(function() { me._remove_dashlet(); })
+ .prependTo(this.element);
+ if (this.element.hasClass('dashlet-selected'))
+ {
+ this.closeBox.show();
+ }
+ else
+ {
+ this.closeBox.hide();
+ }
+
+ },
+
// called when created, and later when changing options
_refresh: function()
{
@@ -47,6 +66,7 @@ $(function()
{
// in 1.9 would use _superApply
$.Widget.prototype._setOptions.apply( this, arguments );
+ this._update();
},
// _setOption is called for each individual option that is changing
_setOption: function( key, value )
diff --git a/js/property_field.js b/js/property_field.js
index d11615045..60de58dde 100644
--- a/js/property_field.js
+++ b/js/property_field.js
@@ -8,21 +8,27 @@ $(function()
// default options
options:
{
+ parent_selector: null,
field_id: '',
submit_to: 'index.php',
submit_parameters: {operation: 'async_action'},
do_apply: null,
- do_cancel: null
+ do_cancel: null,
+ auto_apply: false
},
// the constructor
_create: function()
{
- this.element.addClass( "itop-property-field" );
+ var me = this;
+
+ this.element
+ .addClass( "itop-property-field" )
+ .bind('apply_changes.itop-property-field', function(){me._do_apply();} );
+
this.bModified = false;
- var me = this;
if (this.options.field_id != '')
{
$('#'+this.options.field_id).bind('change.itop-property-field', function() { me._on_change(); });
@@ -80,6 +86,10 @@ $(function()
if (new_value != this.value)
{
this.bModified = true;
+ if (this.options.auto_apply)
+ {
+ this._do_apply();
+ }
}
else
{
@@ -105,6 +115,10 @@ $(function()
},
_do_apply: function()
{
+ if (this.options.parent_selector)
+ {
+ $(this.options.parent_selector).trigger('mark_as_modified');
+ }
if (this.options.do_apply)
{
// specific behavior...
@@ -214,15 +228,18 @@ function ValidateWithPattern(sFieldId, bMandatory, sPattern, sFormId)
re = new RegExp(sPattern);
bValid = re.test(currentVal);
}
+ if (oFormValidation[sFormId] == undefined) oFormValidation[sFormId] = [];
if (!bValid)
{
$('#v_'+sFieldId).addClass('ui-state-error');
- if (oFormValidation[sFormId] == undefined) oFormValidation[sFormId] = [];
oFormValidation[sFormId].push(sFieldId);
}
else
{
$('#v_'+sFieldId).removeClass('ui-state-error');
+ // Remove the element from the array
+ iFieldIdPos = oFormValidation[sFormId].indexOf(sFieldId);
+ oFormValidation[sFormId].splice(iFieldIdPos, 1);
}
}
diff --git a/pages/ajax.render.php b/pages/ajax.render.php
index 7239cb6c5..3d9679379 100644
--- a/pages/ajax.render.php
+++ b/pages/ajax.render.php
@@ -696,7 +696,7 @@ try
// but is executed BEFORE all 'ready_scripts'
$oForm = $oDashlet->GetForm(); // Rebuild the form since the values/content changed
$oForm->SetSubmitParams(utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php', array('operation' => 'update_dashlet_property'));
- $sHtml = addslashes($oForm->RenderAsPropertySheet($oPage, true /* bReturnHtml */));
+ $sHtml = addslashes($oForm->RenderAsPropertySheet($oPage, true /* bReturnHtml */, ':itop-dashboard'));
$sHtml = str_replace("\n", '', $sHtml);
$sHtml = str_replace("\r", '', $sHtml);
$oPage->add_script("$('#dashlet_properties_$sDashletId').html('$sHtml')"); // in ajax web page add_script has the same effect as add_ready_script // but is executed BEFORE all 'ready_scripts'
@@ -742,14 +742,14 @@ try
$sHtml = str_replace("\n", '', $sHtml);
$sHtml = str_replace("\r", '', $sHtml);
- $oPage->add_script("$('#dashlet_$sDashletId').html('$sHtml')"); // in ajax web page add_script has the same effect as add_ready_script
+ $oPage->add_script("$('#dashlet_$sDashletId').html('$sHtml');"); // in ajax web page add_script has the same effect as add_ready_script
// but is executed BEFORE all 'ready_scripts'
}
if ($oDashlet->IsFormRedrawNeeded())
{
$oForm = $oDashlet->GetForm(); // Rebuild the form since the values/content changed
$oForm->SetSubmitParams(utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php', array('operation' => 'update_dashlet_property'));
- $sHtml = addslashes($oForm->RenderAsPropertySheet($oPage, true /* bReturnHtml */));
+ $sHtml = addslashes($oForm->RenderAsPropertySheet($oPage, true /* bReturnHtml */, ':itop-dashboard'));
$sHtml = str_replace("\n", '', $sHtml);
$sHtml = str_replace("\r", '', $sHtml);
$oPage->add_script("$('#dashlet_properties_$sDashletId').html('$sHtml')"); // in ajax web page add_script has the same effect as add_ready_script // but is executed BEFORE all 'ready_scripts'