I've found opaque reference to a <hint/>
tag in system.xml
files. What's the deal with this tag? Is its use documented anywhere?
adminhtml system.xml – How to Use the ‘Hint’ Tag in system.xml
adminhtmlsystem.xml
Related Solutions
When defining your field in your system.xml
file you can specify the source_model
of this field. For example the source model for the standard yes no drop down is as follows.
<source_model>adminhtml/system_config_source_yesno</source_model>
This will tell the field to use the model Mage_Adminhtml_Model_System_Config_Source_Yesno
for building the options. Inside this model you will see the function toOptionArray
which
class Mage_Adminhtml_Model_System_Config_Source_Yesno
{
/**
* Options getter
*
* @return array
*/
public function toOptionArray()
{
return array(
array('value' => 1, 'label'=>Mage::helper('adminhtml')->__('Yes')),
array('value' => 0, 'label'=>Mage::helper('adminhtml')->__('No')),
);
}
}
So using this as a basis for your requirements what you can do is set-up your own model for example Your_Module_Model_System_Config_Region
, then you would simply need to use your table's collection to build the correct structure in the function toOptionArray
.
As far as loading one source model based on the value of another you have two options.
Using Frontend Models
Perfect example of this is the tax options for country and region under app/code/core/Mage/Tax/etc/system.xml
.
Basically what you need to do is create a frontend_model that extends Mage_Adminhtml_Block_System_Config_Form_Field
. This block will only need to have the function _getElementHtml
. What you can do then is call the parent function so the default select is created and show and then add some javascript that will update this select based on when another element has changed.
In the tax example it uses the standard RegionUpdater
but it is possible to work with the more generic SelectUpdater
which allows for one select element to change another.
Now I will show you in depth how this can be achieved. In this example I am using the magento standard country and region collections but you can easily plug in your own collections when needed.
Firstly you will need the system.xml, this should add the correct frontend_model
and source_model
references:
<config>
<tabs>
<example>
<label>Example</label>
</example>
</tabs>
<sections>
<example translate="label" module="example_frontendmodel">
<class>separator-top</class>
<label>Example</label>
<tab>example</tab>
<sort_order>100</sort_order>
<show_in_default>1</show_in_default>
<show_in_website>1</show_in_website>
<show_in_store>1</show_in_store>
<groups>
<frontend_model translate="label">
<label>Frontend Model</label>
<sort_order>30</sort_order>
<show_in_default>1</show_in_default>
<show_in_website>1</show_in_website>
<show_in_store>1</show_in_store>
<fields>
<country translate="label">
<label>Default Country</label>
<frontend_type>select</frontend_type>
<source_model>example_frontendmodel/system_config_source_country</source_model>
<sort_order>10</sort_order>
<show_in_default>1</show_in_default>
<show_in_website>1</show_in_website>
<show_in_store>1</show_in_store>
</country>
<region translate="label">
<label>Default State</label>
<frontend_type>select</frontend_type>
<frontend_model>example_frontendmodel/adminhtml_frontend_region_updater</frontend_model>
<source_model>example_frontendmodel/system_config_source_region</source_model>
<sort_order>20</sort_order>
<show_in_default>1</show_in_default>
<show_in_website>1</show_in_website>
<show_in_store>1</show_in_store>
</region>
</fields>
</frontend_model>
</groups>
</example>
</sections>
</config>
For this example the two source_models are the same as the standard Mage_Tax
source models so there is nothing special there. But you could here load all the options you need for from your tables.
The next thing of importance is the frontend_model
here what you do is extend the class Mage_Adminhtml_Block_System_Config_Form_Field
and create your own _getElementHtml
function. This function will call the parent _getElementHtml
and set a variable with the returned html. Then you can build your javascript using the SelectUpdater
you can see from the init, in file js/mage/adminhtml/form.js
, that you need to specify the following.
- The first select element's id,
- The second select element's id,
- A default message for use on the second select when the first one is not selected,
- A message to use if the selection from the first select element does not have a match in the second,
- The values for the second element json encoded,
- The current selected item,
My example looks as follows.
<?php
class Example_FrontendModel_Block_Adminhtml_Frontend_Region_Updater
extends Mage_Adminhtml_Block_System_Config_Form_Field
{
protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element)
{
$html = parent::_getElementHtml($element);
$js = '<script type="text/javascript">
var updater = new SelectUpdater(
"example_frontend_model_country",
"example_frontend_model_region",
"none",
"none",
%s,
false
);
updater.update();
</script>';
$html .= sprintf($js, $this->helper('example_frontendmodel')->getRegionJson());
return $html;
}
}
The values are built from my helper and replaced into the html string just before being returned. The easiest way to build the values is to create an array of the following format.
array(
'first_select_value1' => array(
'second_select_value1' => 'second_select_text1',
'second_select_value2' => 'second_select_text2',
),
'first_select_value2' => array(
'second_select_value3' => 'second_select_text3',
'second_select_value4' => 'second_select_text4',
)
)
Here is my example's helper for building the values, but you could easily extend it to use what ever collections you need for your need.
<?php
class Example_FrontendModel_Helper_Data extends Mage_Core_Helper_Abstract {
public function getRegionJson()
{
/**
* array(
* 'first_value' => array(
* 'second_value' => 'second_txt'
* )
* )
*/
$countryIds = array();
$countryCollection = Mage::getModel('directory/country')->getResourceCollection()
->loadByStore();
foreach ($countryCollection as $country) {
$countryIds[] = $country->getCountryId();
}
$collection = Mage::getModel('directory/region')->getResourceCollection()
->addCountryFilter($countryIds)
->load();
$regions = array();
foreach ($collection as $region) {
if (!$region->getRegionId()) {
continue;
}
$regions[$region->getCountryId()][$region->getRegionId()] = $this->__($region->getName());
}
return Mage::helper('core')->jsonEncode($regions);
}
}
So the easy but not recommended way is to put your element under app/code/local/Varien/Data/Form/Element/. As Magento is simply using the order of include_paths and will first look into the local folder, before it goes to the lib folder.
But the wiring to Varien_Data_Form_Element is just a fallback which is defined in lib\Varien\Data\Form\Abstract.php.
public function addField($elementId, $type, $config, $after=false)
{
if (isset($this->_types[$type])) {
$className = $this->_types[$type];
}
else {
$className = 'Varien_Data_Form_Element_'.ucfirst(strtolower($type));
}
So the question is where can you defined that _types array? There is an addType method in the same class which can be called. When you search for that method you might stumble over an iteration of _getAdditionalElementTypes on the form block in app\code\core\Mage\Adminhtml\Block\Widget\Form.php
protected function _addElementTypes(Varien_Data_Form_Abstract $baseElement)
{
$types = $this->_getAdditionalElementTypes();
foreach ($types as $code => $className) {
$baseElement->addType($code, $className);
}
}
The default implementation of _getAdditionalElementTypes just returns an empty array, but there is already some working implementation in the Magento Core in Mage_Adminhtml_Block_Sales_Order_Create_Form_Abstract.
So that example looks like:
protected function _getAdditionalFormElementTypes()
{
return array(
'file' => Mage::getConfig()->getBlockClassName('adminhtml/customer_form_element_file'),
'image' => Mage::getConfig()->getBlockClassName('adminhtml/customer_form_element_image'),
'boolean' => Mage::getConfig()->getBlockClassName('adminhtml/customer_form_element_boolean'),
);
}
And even in your form which you need to extend: Mage_Adminhtml_Block_System_Config_Form
protected function _getAdditionalElementTypes()
{
return array(
'export' => Mage::getConfig()->getBlockClassName('adminhtml/system_config_form_field_export'),
'import' => Mage::getConfig()->getBlockClassName('adminhtml/system_config_form_field_import'),
'allowspecific' => Mage::getConfig()
->getBlockClassName('adminhtml/system_config_form_field_select_allowspecific'),
'image' => Mage::getConfig()->getBlockClassName('adminhtml/system_config_form_field_image'),
'file' => Mage::getConfig()->getBlockClassName('adminhtml/system_config_form_field_file')
);
}
Based on that you should be able to implement your own _getAdditonalFormElementTypes by rewriting the System_Config_Form which can then handle new frontend types in your config.
€: Thinking about the question of Fabian it should also be possible by using an observer. The addType method on the form element is public, so you can call that and add custom types from and observer. You need to listen to an event in the form object and in the observer add your custom types. Just make sure this happens before _initFields, which is called from initForm. So a good point can be the prepare_layout events which are called before the initForm method (see Mage_Adminhtml_Block_System_Config_Edit->initForm and Mage_Core_Block_Abstract->setLayout)
Best Answer
I'm not sure about EE, but in CE this is a vestigial tag from a never completed help system. The intent appears to have been to give each form field in the System configuration section a small "hint", or help text.
The help text is added when the field element is created
That first line reads the value of a
<hint/>
insystem.xml
.Next, when Magento renders the field as HTML, the last thing it does is add the hint in a nested div.
Finally, there's a small bit of javascript that's called on an admin page load.
This javascript sets up event handlers such that mousing in or out of the help text will make it appear. The intent being this "hint" would help users understand what each field does.
The problem? The HTML/CSS on the backend makes the hint
td
a single pixel wide. This prevents anyone mousing over the hint to view it. Try adding a hint to your field configuration and then running the following from your browser's javascript consoleYou'll see something like this.
(the This is a hint text).
I've always chalked this up as one of those "Best Laid Plans" thing that got dropped once Magento launched.