- added the ability to create objects pointed by ExtKeys even when the edit mode is a drop-down list and not an autocomplete

- made this behavior configurable globally or per external key, using the config-flag/option: allow_target_creation.

SVN:trunk[987]
This commit is contained in:
Denis Flaven
2010-11-29 10:35:18 +00:00
parent c428e07c4b
commit 4a6bc8a896
6 changed files with 124 additions and 108 deletions

View File

@@ -1052,10 +1052,10 @@ EOF
if (!$oAttDef->IsExternalField())
{
$sMandatory = 'false';
$bMandatory = 'false';
if ( (!$oAttDef->IsNullAllowed()) || ($iFlags & OPT_ATT_MANDATORY))
{
$sMandatory = 'true';
$bMandatory = 'true';
}
$sValidationField = "<span id=\"v_{$iId}\"></span>";
$sHelpText = $oAttDef->GetHelpOnEdition();
@@ -1085,7 +1085,7 @@ EOF
break;
case 'HTML':
$oWidget = new UIHTMLEditorWidget($iId, $sAttCode, $sNameSuffix, $sHelpText, $sValidationField, $value, $sMandatory);
$oWidget = new UIHTMLEditorWidget($iId, $sAttCode, $sNameSuffix, $sHelpText, $sValidationField, $value, $bMandatory);
$sHTMLValue = $oWidget->Display($oPage, $aArgs);
break;
@@ -1128,38 +1128,11 @@ EOF
$aEventsList[] ='validate';
$aEventsList[] ='change';
// #@# todo - add context information (depending on dimensions)
$aAllowedValues = MetaModel::GetAllowedValues_att($sClass, $sAttCode, $aArgs);
$iFieldSize = $oAttDef->GetMaxSize();
$iMaxComboLength = $oAttDef->GetMaximumComboLength();
if (count($aAllowedValues) >= $iMaxComboLength)
{
// too many choices, use an autocomplete
$oWidget = new UIAutoCompleteWidget($sAttCode, $sClass, $oAttDef->GetLabel(), $aAllowedValues, $value, $iId, $sNameSuffix, $sFieldPrefix, $sFormPrefix);
$sHTMLValue = $oWidget->Display($oPage, $aArgs);
}
else
{
// Few choices, use a normal 'select'
// In case there are no valid values, the select will be empty, thus blocking the user from validating the form
$sHTMLValue = "<select title=\"$sHelpText\" name=\"attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}\" id=\"$iId\">\n";
$sHTMLValue .= "<option value=\"\">".Dict::S('UI:SelectOne')."</option>\n";
foreach($aAllowedValues as $key => $display_value)
{
if ((count($aAllowedValues) == 1) && ($sMandatory == 'true') )
{
// When there is only once choice, select it by default
$sSelected = ' selected';
}
else
{
$sSelected = ($value == $key) ? ' selected' : '';
}
$sHTMLValue .= "<option value=\"$key\"$sSelected>$display_value</option>\n";
}
$sHTMLValue .= "</select>&nbsp;{$sValidationField}\n";
}
$oWidget = new UIExtKeyWidget($sAttCode, $sClass, $oAttDef->GetLabel(), $aAllowedValues, $value, $iId, $bMandatory, $sNameSuffix, $sFieldPrefix, $sFormPrefix);
$sHTMLValue = $oWidget->Display($oPage, $aArgs);
break;
case 'String':
@@ -1175,7 +1148,7 @@ EOF
$sHTMLValue .= "<option value=\"\">".Dict::S('UI:SelectOne')."</option>\n";
foreach($aAllowedValues as $key => $display_value)
{
if ((count($aAllowedValues) == 1) && ($sMandatory == 'true') )
if ((count($aAllowedValues) == 1) && ($bMandatory == 'true') )
{
// When there is only once choice, select it by default
$sSelected = ' selected';
@@ -1205,7 +1178,7 @@ EOF
{
$sNullValue = "'$sNullValue'"; // Add quotes to turn this into a JS string if it's not a number
}
$oPage->add_ready_script("$('#$iId').bind('".implode(' ', $aEventsList)."', function(evt, sFormId) { return ValidateField('$iId', '$sPattern', $sMandatory, sFormId, $sNullValue) } );\n"); // Bind to a custom event: validate
$oPage->add_ready_script("$('#$iId').bind('".implode(' ', $aEventsList)."', function(evt, sFormId) { return ValidateField('$iId', '$sPattern', $bMandatory, sFormId, $sNullValue) } );\n"); // Bind to a custom event: validate
}
$aDependencies = MetaModel::GetDependentAttributes($sClass, $sAttCode); // List of attributes that depend on the current one
if (count($aDependencies) > 0)

View File

@@ -14,7 +14,7 @@
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Class UIAutoCompleteWidget
* Class UIExtKeyWidget
* UI wdiget for displaying and editing external keys when
* A simple drop-down list is not enough...
*
@@ -61,7 +61,7 @@
require_once(APPROOT.'/application/webpage.class.inc.php');
require_once(APPROOT.'/application/displayblock.class.inc.php');
class UIAutoCompleteWidget
class UIExtKeyWidget
{
protected static $iWidgetIndex = 0;
protected $sAttCode;
@@ -69,7 +69,7 @@ class UIAutoCompleteWidget
protected $iId;
protected $sTitle;
public function __construct($sAttCode, $sClass, $sTitle, $aAllowedValues, $value, $iInputId, $sNameSuffix = '', $sFieldPrefix = '', $sFormPrefix = '')
public function __construct($sAttCode, $sClass, $sTitle, $aAllowedValues, $value, $iInputId, $bMandatory, $sNameSuffix = '', $sFieldPrefix = '', $sFormPrefix = '')
{
self::$iWidgetIndex++;
$this->sAttCode = $sAttCode;
@@ -83,6 +83,7 @@ class UIAutoCompleteWidget
$this->sTargetClass = $this->oAttDef->GetTargetClass();
$this->sTitle = $sTitle;
$this->sFormPrefix = $sFormPrefix;
$this->bMandatory = $bMandatory;
}
/**
@@ -93,50 +94,74 @@ class UIAutoCompleteWidget
*/
public function Display(WebPage $oPage, $aArgs = array())
{
if ($this->oAttDef->IsNull($this->value)) // Null values are displayed as ''
$bCreate = (UserRights::IsActionAllowed($this->sTargetClass, UR_ACTION_BULK_MODIFY) && $this->oAttDef->AllowTargetCreation());
if (count($this->aAllowedValues) < $this->oAttDef->GetMaximumComboLength())
{
$sDisplayValue = '';
// Few choices, use a normal 'select'
$sSelectMode = 'true';
// In case there are no valid values, the select will be empty, thus blocking the user from validating the form
$sHTMLValue = "<select title=\"$sHelpText\" name=\"attr_{$this->sFieldPrefix}{$this->sAttCode}{$this->sNameSuffix}\" id=\"$this->iId\">\n";
$sHTMLValue .= "<option value=\"\">".Dict::S('UI:SelectOne')."</option>\n";
foreach($this->aAllowedValues as $key => $display_value)
{
if ((count($this->aAllowedValues) == 1) && ($this->bMandatory == 'true') )
{
// When there is only once choice, select it by default
$sSelected = ' selected';
}
else
{
$sSelected = ($this->value == $key) ? ' selected' : '';
}
$sHTMLValue .= "<option value=\"$key\"$sSelected>$display_value</option>\n";
}
$sHTMLValue .= "</select>\n";
}
else
{
$sDisplayValue = $this->GetObjectName($this->value);
}
$bCreate = UserRights::IsActionAllowed($this->sTargetClass, UR_ACTION_BULK_MODIFY);
$sMessage = Dict::S('UI:Message:EmptyList:UseSearchForm');
$sFormPrefix = $this->sFormPrefix;
$oPage->add_ready_script(
<<<EOF
oACWidget_{$this->iId} = new AutocompleteWidget('$this->iId', '$this->sClass', '$this->sAttCode', '$this->sNameSuffix', oWizardHelper$sFormPrefix);
oACWidget_{$this->iId}.emptyHtml = "<div style=\"background: #fff; border:0; text-align:center; vertical-align:middle;\"><p>$sMessage</p></div>";
EOF
);
$iMinChars = $this->oAttDef->GetMinAutoCompleteChars();
$iFieldSize = $this->oAttDef->GetMaxSize();
// Too many choices, use an autocomplete
$sSelectMode = 'false';
if ($this->oAttDef->IsNull($this->value)) // Null values are displayed as ''
{
$sDisplayValue = '';
}
else
{
$sDisplayValue = $this->GetObjectName($this->value);
}
$sMessage = Dict::S('UI:Message:EmptyList:UseSearchForm');
$sFormPrefix = $this->sFormPrefix;
$iMinChars = $this->oAttDef->GetMinAutoCompleteChars();
$iFieldSize = $this->oAttDef->GetMaxSize();
// the input for the auto-complete
$sHTMLValue = "<input count=\"".count($this->aAllowedValues)."\" type=\"text\" id=\"label_$this->iId\" size=\"30\" maxlength=\"$iFieldSize\" value=\"$sDisplayValue\"/>&nbsp;";
$sHTMLValue .= "<a class=\"no-arrow\" href=\"javascript:oACWidget_{$this->iId}.Search();\"><img style=\"border:0;vertical-align:middle;\" src=\"../images/mini_search.gif\" /></a>&nbsp;";
// another hidden input to store & pass the object's Id
$sHTMLValue .= "<input type=\"hidden\" id=\"$this->iId\" name=\"attr_{$this->sFieldPrefix}{$this->sAttCode}{$this->sNameSuffix}\" value=\"$this->value\" />\n";
// Scripts to start the autocomplete and bind some events to it
$oPage->add_ready_script("\$('#label_$this->iId').autocomplete('./ajax.render.php', { scroll:true, minChars:{$iMinChars}, formatItem:formatItem, autoFill:false, matchContains:true, keyHolder:'#{$this->iId}', extraParams:{operation:'autocomplete', sclass:'$this->sClass',attCode:'".$this->sAttCode."'}});");
$oPage->add_ready_script("\$('#label_$this->iId').blur(function() { $(this).search(); } );");
$oPage->add_ready_script("\$('#label_$this->iId').result( function(event, data, formatted) { OnAutoComplete('$this->iId', event, data, formatted); } );");
$oPage->add_ready_script("\$('#ac_dlg_$this->iId').dialog({ width: $(window).width()*0.8, height: $(window).height()*0.8, autoOpen: false, modal: true, title: '$this->sTitle', resizeStop: oACWidget_{$this->iId}.UpdateSizes, close: oACWidget_{$this->iId}.OnClose });\n");
$oPage->add_at_the_end($this->GetSearchDialog($oPage)); // To prevent adding forms inside the main form
// the input for the auto-complete
$sHTMLValue = "<input count=\"".count($this->aAllowedValues)."\" type=\"text\" id=\"label_$this->iId\" size=\"30\" maxlength=\"$iFieldSize\" value=\"$sDisplayValue\"/>&nbsp;";
$sHTMLValue .= "<a class=\"no-arrow\" href=\"javascript:oACWidget_{$this->iId}.Search();\"><img style=\"border:0;vertical-align:middle;\" src=\"../images/mini_search.gif\" /></a>&nbsp;";
}
if ($bCreate)
{
$sHTMLValue .= "<a class=\"no-arrow\" href=\"javascript:oACWidget_{$this->iId}.CreateObject();\"><img style=\"border:0;vertical-align:middle;\" src=\"../images/mini_add.gif\" /></a>&nbsp;";
$oPage->add_at_the_end('<div id="ajax_'.$this->iId.'"></div>');
}
$sHTMLValue .= "<span id=\"v_{$this->iId}\"></span>";
// another hidden input to store & pass the object's Id
$sHTMLValue .= "<input type=\"hidden\" id=\"$this->iId\" name=\"attr_{$this->sFieldPrefix}{$this->sAttCode}{$this->sNameSuffix}\" value=\"$this->value\" />\n";
// Scripts to start the autocomplete and bind some events to it
$oPage->add_ready_script("\$('#label_$this->iId').autocomplete('./ajax.render.php', { scroll:true, minChars:{$iMinChars}, formatItem:formatItem, autoFill:false, matchContains:true, keyHolder:'#{$this->iId}', extraParams:{operation:'autocomplete', sclass:'$this->sClass',attCode:'".$this->sAttCode."'}});");
$oPage->add_ready_script("\$('#label_$this->iId').blur(function() { $(this).search(); } );");
$oPage->add_ready_script("\$('#label_$this->iId').result( function(event, data, formatted) { OnAutoComplete('$this->iId', event, data, formatted); } );");
$oPage->add_ready_script("\$('#ac_dlg_$this->iId').dialog({ width: $(window).width()*0.8, height: $(window).height()*0.8, autoOpen: false, modal: true, title: '$this->sTitle', resizeStop: oACWidget_{$this->iId}.UpdateSizes, close: oACWidget_{$this->iId}.OnClose });\n");
$oPage->add_ready_script("\$('#ac_create_$this->iId').dialog({ width: $(window).width()*0.8, height: $(window).height()*0.8, autoOpen: false, modal: true, title: '$this->sTitle'});\n");
$oPage->add_at_the_end($this->GetSearchDialog($oPage)); // To prevent adding forms inside the main form
if ($bCreate)
{
$oPage->add_at_the_end($this->GetCreationDialog($oPage)); // To prevent adding forms inside the main form
}
$oPage->add_ready_script(
<<<EOF
oACWidget_{$this->iId} = new ExtKeyWidget('$this->iId', '$this->sClass', '$this->sAttCode', '$this->sNameSuffix', $sSelectMode, oWizardHelper{$this->sFormPrefix});
oACWidget_{$this->iId}.emptyHtml = "<div style=\"background: #fff; border:0; text-align:center; vertical-align:middle;\"><p>$sMessage</p></div>";
EOF
);
return $sHTMLValue;
}
@@ -163,23 +188,6 @@ EOF
return $sHTML;
}
protected function GetCreationDialog(WebPage $oPage)
{
$sHTML = '<div id="ac_create_'.$this->iId.'"><div class="wizContainer" style="vertical-align:top;"><div id="dcr_'.$this->iId.'">';
//$sHTML .= "<form id=\"fcr_{$this->iId}\" OnSubmit=\"return oACWidget_{$this->iId}.DoCreate();\">\n";
//cmdbAbstractObject::DisplayCreationForm($oPage, 'Person', null, array());
//$sHTML .= "<input type=\"button\" id=\"btn_create_cancel_{$this->iId}\" value=\"".Dict::S('UI:Button:Cancel')."\" onClick=\"$('#ac_create_{$this->iId}').dialog('close');\">&nbsp;&nbsp;";
//$sHTML .= "<input type=\"button\" id=\"btn_create_ok_{$this->iId}\" value=\"".Dict::S('UI:Button:New')."\" onClick=\"oACWidget_{$this->iId}.DoCreate();\">";
//$sHTML .= "</form>\n";
$sHTML .= '</div></div></div>';
//$oPage->add_ready_script("$('#dc_{$this->iId}').resize(oACWidget_{$this->iId}.UpdateSizes);");
return $sHTML;
}
/**
* Search for objects to be selected
* @param WebPage $oP The page used for the output (usually an AjaxWebPage)
@@ -218,8 +226,11 @@ EOF
*/
public function GetObjectCreationForm(WebPage $oPage)
{
$oPage->add('<div id="ac_create_'.$this->iId.'"><div class="wizContainer" style="vertical-align:top;"><div id="dcr_'.$this->iId.'">');
$oPage->add("<h1>".MetaModel::GetClassIcon($this->sTargetClass)."&nbsp;".Dict::Format('UI:CreationTitle_Class', MetaModel::GetName($this->sTargetClass))."</h1>\n");
cmdbAbstractObject::DisplayCreationForm($oPage, $this->sTargetClass, null, array(), array('formPrefix' => $this->iId, 'noRelations' => true));
$oPage->add('</div></div></div>');
$oPage->add_ready_script("\$('#ac_create_$this->iId').dialog({ width: $(window).width()*0.8, height: 'auto', autoOpen: false, modal: true, title: '$this->sTitle'});\n");
$oPage->add_ready_script("$('#dcr_{$this->iId} form').removeAttr('onsubmit');");
$oPage->add_ready_script("$('#dcr_{$this->iId} form').bind('submit.uilinksWizard', oACWidget_{$this->iId}.DoCreateObject);");
}

View File

@@ -1747,6 +1747,12 @@ class AttributeExternalKey extends AttributeDBFieldVoid
{
return $this->GetOptional('min_autocomplete_chars', utils::GetConfig()->Get('min_autocomplete_chars'));
}
public function AllowTargetCreation()
{
return $this->GetOptional('allow_target_creation', utils::GetConfig()->Get('allow_target_creation'));
}
}
/**

View File

@@ -135,6 +135,14 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'allow_target_creation' => array(
'type' => 'bool',
'description' => 'Displays the + button on external keys to create target objects',
'default' => true,
'value' => true,
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'database_read_only' => array(
'type' => 'bool',
'description' => 'Freeze the data for administration purposes - administrators can still do anything... in appearance!',

View File

@@ -13,7 +13,7 @@
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
function AutocompleteWidget(id, sClass, sAttCode, sSuffix, oWizHelper)
function ExtKeyWidget(id, sClass, sAttCode, sSuffix, bSelectMode, oWizHelper)
{
this.id = id;
this.sClass = sClass;
@@ -23,6 +23,7 @@ function AutocompleteWidget(id, sClass, sAttCode, sSuffix, oWizHelper)
this.emptyOnClose = true; // Workaround for the JQuery dialog being very slow when opening and closing if the content contains many INPUT tags
this.oWizardHelper = oWizHelper;
this.ajax_request = null;
this.bSelectMode = bSelectMode; // true if the edited field is a SELECT, false if it's an autocomplete
var me = this;
this.Init = function()
@@ -194,16 +195,13 @@ function AutocompleteWidget(id, sClass, sAttCode, sSuffix, oWizHelper)
// since responses may arrive in arbitrary order
me.StopPendingRequest();
// Run the query and get the result back directly in JSON
// Run the query and get the result back directly in HTML
me.ajax_request = $.post( 'ajax.render.php', theMap,
function(data)
{
$('#dcr_'+me.id).html(data);
// Adjust the height of the dialog
$('#ajax_'+me.id).html(data);
$('#ac_create_'+me.id).dialog('open');
var h = $('#ac_create_'+me.id+' .wizContainer').outerHeight();
$('#ac_create_'+me.id).dialog( "option", "height", h+55 ); // space for dialog title and padding...
$('#ac_create_'+me.id).dialog( "option", "close", function() { $('#label_'+me.id).removeClass('ac_loading'); $('#label_'+me.id).focus(); } );
$('#ac_create_'+me.id).dialog( "option", "close", me.OnCloseCreateObject );
// Modify the action of the cancel button
$('#ac_create_'+me.id+' button.cancel').unbind('click').click( me.CloseCreateObject );
me.ajax_request = null;
@@ -215,7 +213,15 @@ function AutocompleteWidget(id, sClass, sAttCode, sSuffix, oWizHelper)
this.CloseCreateObject = function()
{
$('#ac_create_'+me.id).dialog( "close" );
$('#dcr_'+me.id).html('');
}
this.OnCloseCreateObject = function()
{
$('#label_'+me.id).removeClass('ac_loading');
$('#label_'+me.id).focus();
$('#ac_create_'+me.id).dialog("destroy");
$('#ac_create_'+me.id).remove();
$('#ajax_'+me.id).html('');
}
this.DoCreateObject = function()
@@ -256,11 +262,22 @@ function AutocompleteWidget(id, sClass, sAttCode, sSuffix, oWizHelper)
me.ajax_request = $.post( 'ajax.render.php', theMap,
function(data)
{
$('#label_'+me.id).val(data.name);
$('#'+me.id).val(data.id);
if (me.bSelectMode)
{
// Add the newly created object to the drop-down list and select it
$('<option/>', { value : data.id }).text(data.name).appendTo('#'+me.id);
$('#'+me.id+' option[value="'+data.id+'"]').attr('selected', 'selected');
$('#'+me.id).focus();
}
else
{
// Put the value corresponding to the newly created object in the autocomplete
$('#label_'+me.id).val(data.name);
$('#'+me.id).val(data.id);
$('#label_'+me.id).removeClass('ac_loading');
$('#label_'+me.id).focus();
}
$('#'+me.id).trigger('validate');
$('#label_'+me.id).removeClass('ac_loading');
$('#label_'+me.id).focus();
me.ajax_request = null;
},
'json'

View File

@@ -72,7 +72,7 @@ try
$oWidget->SearchObjectsToAdd($oPage, $sRemoteClass, $aAlreadyLinked);
break;
// ui.autocompletewidget
// ui.extkeywidget
case 'searchObjectsToSelect':
$sTargetClass = utils::ReadParam('sRemoteClass', '');
$sAttCode = utils::ReadParam('sAttCode', '');
@@ -82,11 +82,11 @@ try
$oWizardHelper = WizardHelper::FromJSON($sJson);
$oObj = $oWizardHelper->GetTargetObject();
$aAllowedValues = MetaModel::GetAllowedValues_att($sClass, $sAttCode, array('this' => $oObj));
$oWidget = new UIAutocompleteWidget($sAttCode, $sClass, '', $aAllowedValues, $oObj->Get($sAttCode), $iInputId, $sSuffix, '');
$oWidget = new UIExtKeyWidget($sAttCode, $sClass, '', $aAllowedValues, $oObj->Get($sAttCode), $iInputId, false, $sSuffix, '');
$oWidget->SearchObjectsToSelect($oPage, $sTargetClass);
break;
// ui.autocompletewidget
// ui.extkeywidget
case 'objectCreationForm':
$sTargetClass = utils::ReadParam('sRemoteClass', '');
$sAttCode = utils::ReadParam('sAttCode', '');
@@ -96,11 +96,11 @@ try
$oWizardHelper = WizardHelper::FromJSON($sJson);
$oObj = $oWizardHelper->GetTargetObject();
$aAllowedValues = MetaModel::GetAllowedValues_att($sClass, $sAttCode, array('this' => $oObj));
$oWidget = new UIAutocompleteWidget($sAttCode, $sClass, '', $aAllowedValues, $oObj->Get($sAttCode), $iInputId, $sSuffix, '');
$oWidget = new UIExtKeyWidget($sAttCode, $sClass, '', $aAllowedValues, $oObj->Get($sAttCode), $iInputId, false, $sSuffix, '');
$oWidget->GetObjectCreationForm($oPage);
break;
// ui.autocompletewidget
// ui.extkeywidget
case 'doCreateObject':
$sTargetClass = utils::ReadParam('sRemoteClass', '');
$sAttCode = utils::ReadParam('sAttCode', '');
@@ -111,19 +111,19 @@ try
$oObj = $oWizardHelper->GetTargetObject();
$aAllowedValues = MetaModel::GetAllowedValues_att($sClass, $sAttCode, array('this' => $oObj));
// The iInputId of the autocomplete is the prefix for the form used to create the target object
$oWidget = new UIAutocompleteWidget($sAttCode, $sClass, '', $aAllowedValues, null, $iInputId, $sSuffix, $oWizardHelper->GetFormPrefix());
$oWidget = new UIExtKeyWidget($sAttCode, $sClass, '', $aAllowedValues, null, $iInputId, false, $sSuffix, $oWizardHelper->GetFormPrefix());
$aResult = $oWidget->DoCreateObject($oPage);
echo json_encode($aResult);
break;
// ui.autocompletewidget
// ui.extkeywidget
case 'getObjectName':
$sTargetClass = utils::ReadParam('sTargetClass', '');
$sAttCode = utils::ReadParam('sAttCode', '');
$iInputId = utils::ReadParam('iInputId', '');
$iObjectId = utils::ReadParam('iObjectId', '');
$sSuffix = utils::ReadParam('sSuffix', '');
$oWidget = new UIAutocompleteWidget($sAttCode, $sClass, '', array(), '', $iInputId, $sSuffix, '');
$oWidget = new UIExtKeyWidget($sAttCode, $sClass, '', array(), '', $iInputId, $sSuffix, '');
$sName = $oWidget->GetObjectName($iObjectId);
echo json_encode(array('name' => $sName));
break;
@@ -398,6 +398,7 @@ try
}
catch (Exception $e)
{
echo $e->GetMessage();
IssueLog::Error($e->getMessage());
}