Magento Admin Interface Buttons
We recently had one of our clients ring up telling us one of the buttons in their admin interface had gone missing. As we had just added a couple of plugins (after testing on a development site of course) there were all sorts of mutterings around plugin authors and their programming practices, however, it turns out not to be a plugin issue after all, but rather a core Magento issue.
After some delving around I found that a plugin (that had been in place for some time) was all of a sudden trying to add a button into the same location as a core button (in this case the Credit Memo button on the invoice page). It took me a while to work out why and it was nothing to do with the plugin really.
The buttons in the admin area are controlled by a widget:
app/code/local/Mage/Adminhtml/Block/Widget/Container.php
Within the container code, there is a method to add a button (addButton) and another to remove a button (removeButton), all very standard and logical. There is also code to return the relevant HTML for the buttons as well, it’s the interaction between the 3 that causes the issue.
In our case the buttons were added correctly (all of them), however the older plugin (one designed to replace the internal print option with a customisable PDF version), removed the old print button and added its own print button, the add code (below) has an issue when combined with the returned HTML:
protected function _addButton($id, $data, $level = 0, $sortOrder = 0, $area = 'header')
{
if (!isset($this->_buttons[$level])) {
$this->_buttons[$level] = array();
}
$this->_buttons[$level][$id] = $data;
$this->_buttons[$level][$id]['area'] = $area;
if ($sortOrder) {
$this->_buttons[$level][$id]['sort_order'] = $sortOrder;
} else {
$this->_buttons[$level][$id]['sort_order'] = count($this->_buttons[$level]) * 10;
}
return $this;
}
public function getButtonsHtml($area = null)
{
$out = '';
foreach ($this->_buttons as $level => $buttons) {
$_buttons = array();
foreach ($buttons as $id => $data) {
$_buttons[$data['sort_order']]['id'] = $id;
$_buttons[$data['sort_order']]['data'] = $data;
}
ksort($_buttons);
snipped
You will notice that when the buttons are output, the buttons are re-arranged by sort order and you are only allowed 1 button per “sort order”. The automatic sort order, is determined directly by the number of items in the current “level”, if all you do is add items you will be fine, but if a module removes one first and then adds another one, the new item will have the same sort order as the previous button and will, therefore, be overwritten at output stage.
There are (at least) two ways to solve this.
1. Change the code so that the sort order is determined at output time (in-consistent you may get things out in a different order than they went in)
2. Keep a counter as you add buttons (and do not decrement as you remove them) – this is the option we chose here:
protected function _addButton($id, $data, $level = 0, $sortOrder = 0, $area = 'header')
{
if (!isset($this->_buttons[$level])) {
$this->_buttons[$level] = array();
$this->buttoncounter[$level]=1;
}else
{
$this->buttoncounter[$level]++;
}
$this->_buttons[$level][$id] = $data;
$this->_buttons[$level][$id]['area'] = $area;
if ($sortOrder) {
$this->_buttons[$level][$id]['sort_order'] = $sortOrder;
} else {
$this->_buttons[$level][$id]['sort_order'] = $this->buttoncounter[$level] * 10;
}
return $this;
}
This keeps the sort order incrementing and unique.
Job done!