From 2eddb57cbdd6b8dc3b9eeca1eb3be8f4b1aba8bf Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Fri, 21 Sep 2012 15:15:05 +0000 Subject: [PATCH] Dashboards - Fixed issues + added 3 confirmation (on exit, save or cancel) SVN:trunk[2199] --- application/dashboard.class.inc.php | 60 +++++++++++++++++++++++--- application/dashlet.class.inc.php | 4 +- application/forms.class.inc.php | 24 +++++++++-- css/light-grey.css | 4 +- dictionaries/dictionary.itop.ui.php | 2 + dictionaries/fr.dictionary.itop.ui.php | 2 + js/dashboard.js | 31 ++++++++++++- js/dashlet.js | 26 +++++++++-- js/property_field.js | 25 +++++++++-- pages/ajax.render.php | 6 +-- 10 files changed, 161 insertions(+), 23 deletions(-) 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(''); } } @@ -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'