Prerequisites to the portal forms:

- finalize form fields in the order of their dependencies
- introduced the SelectObjectField which will implement an autocomplete (currently remains a drop-down whatever the number of items)
- code refactoring

SVN:trunk[3951]
This commit is contained in:
Romain Quetiez
2016-03-16 09:09:30 +00:00
parent 1c90cd2312
commit 92d9c778e5
7 changed files with 199 additions and 31 deletions

View File

@@ -30,6 +30,7 @@ require_once APPROOT . 'sources/form/field/stringfield.class.inc.php';
require_once APPROOT . 'sources/form/field/textareafield.class.inc.php';
require_once APPROOT . 'sources/form/field/multiplechoicesfield.class.inc.php';
require_once APPROOT . 'sources/form/field/selectfield.class.inc.php';
require_once APPROOT . 'sources/form/field/selectobjectfield.class.inc.php';
require_once APPROOT . 'sources/form/field/checkboxfield.class.inc.php';
require_once APPROOT . 'sources/form/field/radiofield.class.inc.php';
require_once APPROOT . 'sources/form/validator/validator.class.inc.php';

View File

@@ -378,5 +378,19 @@ abstract class Field
*
* @return boolean
*/
abstract public function Validate();
public function Validate()
{
$this->SetValid(true);
$this->EmptyErrorMessages();
foreach ($this->GetValidators() as $oValidator)
{
if (!preg_match($oValidator->GetRegExp(true), $this->GetCurrentValue()))
{
$this->SetValid(false);
$this->AddErrorMessage($oValidator->GetErrorMessage());
}
}
return $this->GetValid();
}
}

View File

@@ -0,0 +1,102 @@
<?php
// Copyright (C) 2010-2016 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
namespace Combodo\iTop\Form\Field;
use \Closure;
use Combodo\iTop\Form\Validator\NotEmptyExtKeyValidator;
/**
* Description of SelectObjectField
*
*/
class SelectObjectField extends Field
{
protected $sOqlQuery;
protected $iMaximumComboLength;
protected $iMinAutoCompleteChars;
public function __construct($sId, Closure $onFinalizeCallback = null)
{
parent::__construct($sId, $onFinalizeCallback);
$this->sOqlQuery = null;
$this->iMaximumComboLength = null;
$this->iMinAutoCompleteChars = 3;
}
public function SetOqlQuery($sOqlQuery)
{
$this->sOqlQuery = $sOqlQuery;
}
public function SetMaximumComboLength($iMaximumComboLength)
{
$this->iMaximumComboLength = $iMaximumComboLength;
}
public function SetMinAutoCompleteChars($iMinAutoCompleteChars)
{
$this->iMinAutoCompleteChars = $iMinAutoCompleteChars;
}
/**
* Sets if the field is mandatory or not.
* Setting the value will automatically add/remove a MandatoryValidator to the Field
*
* @param boolean $bMandatory
* @return \Combodo\iTop\Form\Field\Field
*/
public function SetMandatory($bMandatory)
{
// Before changing the property, we check if it was already mandatory. If not, we had the mandatory validator
if ($bMandatory && !$this->bMandatory)
{
$this->AddValidator(new NotEmptyExtKeyValidator());
}
if (!$bMandatory)
{
foreach ($this->aValidators as $iKey => $oValue)
{
if ($oValue::Getname() === NotEmptyExtKeyValidator::GetName())
{
unset($this->aValidators[$iKey]);
}
}
}
$this->bMandatory = $bMandatory;
return $this;
}
public function GetOqlQuery()
{
return $this->sOqlQuery;
}
public function GetMaximumComboLength()
{
return $this->iMaximumComboLength;
}
public function GetMinAutoCompleteChars()
{
return $this->iMinAutoCompleteChars;
}
}

View File

@@ -29,26 +29,4 @@ use \Combodo\iTop\Form\Field\Field;
abstract class TextField extends Field
{
/**
* Checks the validators to see if the field's current value is valid.
* Then sets $bValid and $aErrorMessages.
*
* @return boolean
*/
public function Validate()
{
$this->SetValid(true);
$this->EmptyErrorMessages();
foreach ($this->GetValidators() as $oValidator)
{
if (!preg_match($oValidator->GetRegExp(true), $this->GetCurrentValue()))
{
$this->SetValid(false);
$this->AddErrorMessage($oValidator->GetErrorMessage());
}
}
return $this->GetValid();
}
}

View File

@@ -397,16 +397,47 @@ class Form
}
/**
*
* Finalizes each field, following the dependencies so that a field can compute its value or other properties,
* depending on other fields
*/
public function Finalize()
{
//TODO : Call GetOrderedFields
// Must call OnFinalize on each fields, regarding the dependencies order
// On a SubFormField, will call its own Finalize
foreach ($this->aFields as $sId => $oField)
{
$oField->OnFinalize();
$aFieldList = array(); // Fields ordered by dependence
// Clone the dependency data : $aDependencies will be truncated as the fields are added to the list
$aDependencies = $this->aDependencies;
$bMadeProgress = true; // Safety net in case of circular references
while ($bMadeProgress && count($aFieldList) < count($this->aFields))
{
$bMadeProgress = false;
foreach ($this->aFields as $sId => $oField)
{
if (array_key_exists($sId, $aFieldList)) continue;
if (isset($aDependencies[$sId]) && count($aDependencies[$sId]) > 0) continue;
// Add the field at the end of the list
$aFieldList[$sId] = $oField;
$bMadeProgress = true;
// Track that this dependency has been solved
foreach ($aDependencies as $sImpactedBy => $aSomeFields)
{
foreach ($aSomeFields as $i => $sSomeId)
{
if ($sSomeId == $sId)
{
unset($aDependencies[$sImpactedBy][$i]);
}
}
}
}
}
if (!$bMadeProgress)
{
throw new Exception('Unmet dependencies: '.implode(', ', array_keys($aDependencies)));
}
foreach ($aFieldList as $sId => $oField)
{
$oField->OnFinalize();
}
}

View File

@@ -20,7 +20,6 @@ namespace Combodo\iTop\Renderer\Console;
use Combodo\iTop\Form\Form;
use Combodo\iTop\Renderer\FormRenderer;
use Combodo\iTop\Renderer\RenderingOutput;
use \Dict;
require_once('fieldrenderer/consolesimplefieldrenderer.class.inc.php');
@@ -36,6 +35,7 @@ class ConsoleFormRenderer extends FormRenderer
$this->AddSupportedField('HiddenField', 'ConsoleSimpleFieldRenderer');
$this->AddSupportedField('StringField', 'ConsoleSimpleFieldRenderer');
$this->AddSupportedField('SelectField', 'ConsoleSimpleFieldRenderer');
$this->AddSupportedField('SelectObjectField', 'ConsoleSimpleFieldRenderer');
$this->AddSupportedField('SubFormField', 'ConsoleSubFormFieldRenderer');
}
}

View File

@@ -82,6 +82,46 @@ class ConsoleSimpleFieldRenderer extends FieldRenderer
$oOutput->AddHtml('<span class="form_validation"></span>');
$oOutput->AddHtml('</td>');
break;
case 'Combodo\\iTop\\Form\\Field\\SelectObjectField':
$oOutput->AddHtml('<td class="form-field-content">');
if ($this->oField->GetReadOnly())
{
$oSearch = \DBSearch::FromOQL($this->oField->GetOqlQuery());
$oSearch->AddCondition('id', $this->oField->GetCurrentValue());
$oSet = new DBObjectSet($oSearch);
$oObject = $oSet->Fetch();
if ($oObject)
{
$sCurrentLabel = $oObject->Get('friendlyname');
}
else
{
$sCurrentLabel = '';
}
$oOutput->AddHtml('<input type="hidden" id="'.$this->oField->GetGlobalId().'" value="' . htmlentities($this->oField->GetCurrentValue(), ENT_QUOTES, 'UTF-8') . '"/>');
$oOutput->AddHtml('<span class="form-field-data">'.htmlentities($sCurrentLabel, ENT_QUOTES, 'UTF-8').'</span>');
}
else
{
$oSearch = \DBSearch::FromOQL($this->oField->GetOqlQuery());
$oSet = new \DBObjectSet($oSearch);
$oSet->OptimizeColumnLoad(array($oSearch->GetClassAlias() => array('friendlyname')));
$oOutput->AddHtml('<select class="form-field-data" id="'.$this->oField->GetGlobalId().'">');
$oOutput->AddHtml('<option value="0">'.Dict::S('UI:SelectOne').'</option>');
while ($oObject = $oSet->Fetch())
{
$iObject = $oObject->GetKey();
$sLabel = $oObject->Get('friendlyname');
// Note : The test is a double equal on purpose as the type of the value received from the XHR is not always the same as the type of the allowed values. (eg : string vs int)
$sSelectedAtt = ($this->oField->GetCurrentValue() == $iObject) ? 'selected' : '';
$oOutput->AddHtml('<option value="'.$iObject.'" '.$sSelectedAtt.' >'.htmlentities($sLabel, ENT_QUOTES, 'UTF-8').'</option>');
}
$oOutput->AddHtml('</select>');
}
$oOutput->AddHtml('<span class="form_validation"></span>');
$oOutput->AddHtml('</td>');
break;
}
$oOutput->AddHtml('</tr>');
$oOutput->AddHtml('</table>');
@@ -106,6 +146,7 @@ EOF
);
break;
case 'Combodo\\iTop\\Form\\Field\\SelectField':
case 'Combodo\\iTop\\Form\\Field\\SelectObjectField':
$oOutput->AddJs(
<<<EOF
$("#{$this->oField->GetGlobalId()}").off("change").on("change", function(){
@@ -169,6 +210,7 @@ EOF
switch ($sFieldClass)
{
case 'Combodo\\iTop\\Form\\Field\\SelectField':
case 'Combodo\\iTop\\Form\\Field\\SelectObjectField':
$oOutput->AddJs(
<<<EOF
$("[data-field-id='{$this->oField->GetId()}'][data-form-path='{$this->oField->GetFormPath()}']").form_field('option', 'get_current_value_callback', function(me){ return $(me.element).find('select').val();});