Problem Adding Custom Buttons to the list view #939

Closed
opened 2022-07-20 19:03:59 +00:00 by Chris Paschen · 2 comments
Member

Steps to reproduce the issue

On an existing Admin View
Go to Custom Buttons Tab
Set Add Custom Buttons to YES
Click + to add a Custom Buttons entry
Complete the form (Icon, Name, Target Controller Method name, Target - list, Type (any).
Add a public function with the Controller Method name to the controller method name
Compile
Select an entry (use checkbox) on the list view
Click the new custom button

Expected result

Code in method should run

Actual result

The code does't run

System information (as much as possible)

  • OS Name & Version: WAMP
  • MySql Version: 5.7.31
  • Apache Version: 2.4.46
  • PHP Version: 7.4.9
  • Joomla Version: 3.10.10
  • JCB Version: 2.13.1
  • Browser: firefox dev nightly build

Additional comments

I'm not sure if this has anything to do with it, as I don't know how the funtion actually gets 'called' in the list view, but the method is added to the controllers/profile.php (singular) file, not the controllers/profiles.php (multiple) file.

Also, there doesn't appear to be any explanation that I can find about the TYPE selector - what effect Default / Selection / Only Function has on the processing.

When using Target - Single, the button/code functions as it should (and seemingly independent of the TYPE selection.

Possibly just a quck explanation of how to set-up to get code to run in list view (what settigs to use) would be helpful (or an update of the existing video).

### Steps to reproduce the issue On an existing Admin View Go to Custom Buttons Tab Set Add Custom Buttons to YES Click + to add a Custom Buttons entry Complete the form (Icon, Name, Target Controller Method name, Target - list, Type (any). Add a public function with the Controller Method name to the controller method name Compile Select an entry (use checkbox) on the list view Click the new custom button ### Expected result Code in method should run ### Actual result The code does't run ### System information (as much as possible) - OS Name & Version: WAMP - MySql Version: 5.7.31 - Apache Version: 2.4.46 - PHP Version: 7.4.9 - Joomla Version: 3.10.10 - JCB Version: 2.13.1 - Browser: firefox dev nightly build ### Additional comments I'm not sure if this has anything to do with it, as I don't know how the funtion actually gets 'called' in the list view, but the method is added to the controllers/profile.php (singular) file, not the controllers/profiles.php (multiple) file. Also, there doesn't appear to be any explanation that I can find about the TYPE selector - what effect Default / Selection / Only Function has on the processing. When using Target - Single, the button/code functions as it should (and seemingly independent of the TYPE selection. Possibly just a quck explanation of how to set-up to get code to run in list view (what settigs to use) would be helpful (or an update of the existing video).
Owner

Okay so the three options give you three kinds of behaviour.

  • Default => This button is the same as selection, but is little more intelegent as there use to be only this option, and the other were added to force the different options.
  • Selection => This button will force users to select an item before they can submit the button.
  • Only Function => This button does not force selection of any items.

In reality the code you write in the controller and module is what makes these buttons work, all they do is post to the controller. This means you can always catch whatever was selected in the controller, and all the button does is give you an specific action to apply.

The normal controller code for selection:
image

smartExport

public function smartExport()
{
	// Check for request forgeries
	JSession::checkToken() or die(JText::_('JINVALID_TOKEN'));
	// Get the model
	$model = $this->getModel('Joomla_components');
	// check if export is allowed for this user.
	$model->user = JFactory::getUser();
	if ($model->user->authorise('joomla_component.export_jcb_packages', 'com_[[[component]]]') && $model->user->authorise('core.export', 'com_[[[component]]]'))
	{
		// Get the input
		$input = JFactory::getApplication()->input;
		$pks = $input->post->get('cid', array(), 'array');
		// Sanitize the input
		JArrayHelper::toInteger($pks);
		// check if there is any selections
		if (![[[Component]]]Helper::checkArray($pks))
		{
			// Redirect to the list screen with error.
			$message = JText::_('No components were selected, please make a selection and try again!');
			$this->setRedirect(JRoute::_('index.php?option=com_[[[component]]]&view=joomla_components', false), $message, 'error');
			return;
		}
		// set auto loader
		[[[Component]]]Helper::autoLoader('smart');
		// get the data to export
		if ($model->getSmartExport($pks))
		{
			// set the key string
			if ([[[component]]]Helper::checkString($model->key) && strlen($model->key) == 32)
			{
				$keyNotice = '<h1>' . JText::sprintf('The package key is: <code>%s</code>', $model->key) . '</h1>';
				$keyNotice .= '<p>' . JText::_('Your data is encrypted with a AES 128 bit encryption using the above 32 character key.') . '</h1>';
				// set the package owner info
				if ([[[component]]]Helper::getPackageOwnerValue('owner', $model->info) || [[[component]]]Helper::getPackageOwnerValue('company', $model->info))
				{
					$ownerDetails = [[[component]]]Helper::getPackageOwnerDetailsDisplay($model->info, true);
				}
				else
				{
					$ownerDetails = '<h2>' . JText::_('Package Owner Not Set') . '</h2>';
					$ownerDetails .= '<p>' . JText::_('To change the package owner defaults. Open the <b>JCB Global Options</b>, go to the <b>Company</b> tab and add the correct company details there.') . '</p>';
					$ownerDetails .= '<h3>' . JText::_('You should add the correct owner details.') . '</h3>';
					$ownerDetails .= '<p>' . JText::_('Since the owner details are displayed during <b>import process</b> before adding the key, this way if the user/dev <b>does not</b> have the key they can see <b>where to get it</b>.') . '</p>';
				}
			}
			else
			{
				$keyNotice = '<h1>' . JText::_('This package has no key.') . '</h1>';
				$ownerDetails = '<p>' . JText::_('That means anyone who has this package can install it into JCB. To add an export key simply open the component, go to the tab called <b>settings</b>, bottom right there is a field called <b>Export Key</b>.') . '</p>';
			}
			// Redirect to the list screen with success.
			$message = array();
			$message[] = '<h1>' . JText::_('Export Completed!') . '</h1>';
			$message[] = '<p>' . JText::sprintf('Path to the zipped package is: <code>%s</code> <br />%s %s', $model->zipPath, $keyNotice, $ownerDetails) . '</p>';
			$this->setRedirect(JRoute::_('index.php?option=com_[[[component]]]&view=joomla_components', false), implode('', $message), 'Success');
			return;
		}
		else
		{
			if ([[[component]]]Helper::checkString($model->packagePath))
			{
				// clear all if not successful
				[[[Component]]]Helper::removeFolder($model->packagePath);
			}
			if ([[[component]]]Helper::checkString($model->zipPath))
			{
				// clear all if not successful
				JFile::delete($model->zipPath);
			}				
		}
	}
	// Redirect to the list screen with error.
	$message = JText::_('Export failed, please try again latter!');
	$this->setRedirect(JRoute::_('index.php?option=com_[[[component]]]&view=joomla_components', false), $message, 'error');
	return;
}

cloner

public function cloner()
{
	// Check for request forgeries
	JSession::checkToken() or die(JText::_('JINVALID_TOKEN'));
	// Get the model
	$model = $this->getModel('Joomla_components');
	// check if export is allowed for this user.
	$model->user = JFactory::getUser();
	if ($model->user->authorise('joomla_component.clone', 'com_[[[component]]]') && $model->user->authorise('core.copy', 'com_[[[component]]]'))
	{
		// Get the input
		$input = JFactory::getApplication()->input;
		$pks = $input->post->get('cid', array(), 'array');
		// Sanitize the input
		JArrayHelper::toInteger($pks);
		// check if there is any selections
		if (![[[Component]]]Helper::checkArray($pks))
		{
			// Redirect to the list screen with error.
			$message = JText::_('No component was selected, please make a selection of one component and try again!');
			$this->setRedirect(JRoute::_('index.php?option=com_[[[component]]]&view=joomla_components', false), $message, 'error');
			return;
		}
		// only one component allowed at this time
		elseif (count( (array) $pks) !== 1)
		{
			// Redirect to the list screen with error.
			$message = JText::_('Only one component can be cloned at a time, please select one and try again!');
			$this->setRedirect(JRoute::_('index.php?option=com_[[[component]]]&view=joomla_components', false), $message, 'error');
			return;
		}
		// set the active type
		$model->activeType = 'clone';
		// clone the component and the views and the fields... everything linked to the component.
		if ($model->cloner($pks))
		{
			// clone was successful
			$message = JText::_('The Component with all linked admin views, fields linked to admin views, custom admin views, site views, templates and layouts were cloned successful!');
			$this->setRedirect(JRoute::_('index.php?option=com_[[[component]]]&view=joomla_components', false), $message);
			return;
		}
		// Redirect to the list screen with error.
		$message = JText::_('Clone failed!');
		$this->setRedirect(JRoute::_('index.php?option=com_[[[component]]]&view=joomla_components', false), $message, 'error');
		return;
	}
	// Redirect to the list screen with error.
	$message = JText::_('You do not have permission to clone a component, please contact your system administrator for more help.');
	$this->setRedirect(JRoute::_('index.php?option=com_[[[component]]]&view=joomla_components', false), $message, 'error');
	return;
}

Be sure to add it to the right controller: PHP List view (controller methods)
image

The normal controller code for function only:
image

public function getSnippets()
{
	// Check for request forgeries
	JSession::checkToken() or die(JText::_('JINVALID_TOKEN'));
	// redirect to the import snippets custom admin view
	$this->setRedirect(JRoute::_('index.php?option=com_[[[component]]]&view=get_snippets', false));
	return;
}

Be sure to add it to the right controller: PHP List view (controller methods)
image

As you mabe noticed all these are list view buttons, the single, and both target options as minor changes in the GUI, but the controller side also works with a post method, and can look like this:

public function requestTrustKey()
{		
	// Check for request forgeries.
	JSession::checkToken() or jexit(JText::_('JINVALID_TOKEN'));
	// get the data
	$originalData = $this->input->post->get('jform', array(), 'array');
	if (isset($originalData['id']) && $originalData['id'] > 0  && (isset($originalData['url']) && [[[Component]]]Helper::checkString($originalData['url'])) || (isset($originalData['ip']) && [[[Component]]]Helper::checkString($originalData['ip'])))
	{
		// get the model
		$model = $this->getModel('[[[arg0]]]');
		// get the user object
		if (![[[Component]]]Helper::checkObject($model->user))
		{
			$model->user = JFactory::getUser();
		}
		if ($model->user->authorise('[[[arg0]]].requestTrustKey', 'com_[[[component]]]') && $model->requestTrustKey($originalData))
		{
			// Redirect to the list screen with success.
			$message = JText::sprintf('The trust key has been requested and will be emailed to you (%s) once the trusted user on the remote sync system (%s) has confirmed to allow this request.', $model->user->email, $originalData['name']);
			$this->setRedirect(JRoute::_('index.php?option=com_[[[component]]]&view=[[[arg0]]]&layout=edit&id=' . (int) $originalData['id'], false), $message, 'success');
			return;
		}
	}
	$this->setError(JText::sprintf('The trust key request to %s has failed!', $originalData['name']));
	$this->setMessage($this->getError(), 'error');
	// Redirect back to the list screen.
	$this->setRedirect(JRoute::_('index.php?option=com_[[[component]]]&view=[[[arg0]]]&layout=edit&id=' . (int) $originalData['id'], false));
	return false;
}

Be sure to add it to the right controller: PHP (controller methods)

You will see in most of these controller methods, we validate access, and then call the module... which is basicly how any post should behave.

Anyway to me the actual issue is you need to know how to build your PHP scripting side, and be able to debug this untill it does what you want. What is true is I use this feature in a thousand ways and they have never given me any errors...

Okay so the [three options](https://git.vdm.dev/joomla/Component-Builder/src/branch/staging/admin/helpers/compiler/e_Interpretation.php#L5535) give you three kinds of behaviour. - Default => This button is the same as selection, but is little more intelegent as there use to be only *this option*, and the other were added to force the different options. - Selection => This button will force users to select an item before they can submit the button. - [Only Function](https://git.vdm.dev/joomla/Component-Builder/src/branch/staging/admin/helpers/compiler/e_Interpretation.php#L5704) => This button does not force selection of any items. In reality the code you write in the controller and module is what makes these buttons work, all they do is post to the controller. This means you can always catch whatever was selected in the controller, and all the button does is give you an specific action to apply. The normal controller code for selection: ![image](/attachments/6b44f1bc-bcd3-4841-84a8-fccfeef43790) > smartExport ```php public function smartExport() { // Check for request forgeries JSession::checkToken() or die(JText::_('JINVALID_TOKEN')); // Get the model $model = $this->getModel('Joomla_components'); // check if export is allowed for this user. $model->user = JFactory::getUser(); if ($model->user->authorise('joomla_component.export_jcb_packages', 'com_[[[component]]]') && $model->user->authorise('core.export', 'com_[[[component]]]')) { // Get the input $input = JFactory::getApplication()->input; $pks = $input->post->get('cid', array(), 'array'); // Sanitize the input JArrayHelper::toInteger($pks); // check if there is any selections if (![[[Component]]]Helper::checkArray($pks)) { // Redirect to the list screen with error. $message = JText::_('No components were selected, please make a selection and try again!'); $this->setRedirect(JRoute::_('index.php?option=com_[[[component]]]&view=joomla_components', false), $message, 'error'); return; } // set auto loader [[[Component]]]Helper::autoLoader('smart'); // get the data to export if ($model->getSmartExport($pks)) { // set the key string if ([[[component]]]Helper::checkString($model->key) && strlen($model->key) == 32) { $keyNotice = '<h1>' . JText::sprintf('The package key is: <code>%s</code>', $model->key) . '</h1>'; $keyNotice .= '<p>' . JText::_('Your data is encrypted with a AES 128 bit encryption using the above 32 character key.') . '</h1>'; // set the package owner info if ([[[component]]]Helper::getPackageOwnerValue('owner', $model->info) || [[[component]]]Helper::getPackageOwnerValue('company', $model->info)) { $ownerDetails = [[[component]]]Helper::getPackageOwnerDetailsDisplay($model->info, true); } else { $ownerDetails = '<h2>' . JText::_('Package Owner Not Set') . '</h2>'; $ownerDetails .= '<p>' . JText::_('To change the package owner defaults. Open the <b>JCB Global Options</b>, go to the <b>Company</b> tab and add the correct company details there.') . '</p>'; $ownerDetails .= '<h3>' . JText::_('You should add the correct owner details.') . '</h3>'; $ownerDetails .= '<p>' . JText::_('Since the owner details are displayed during <b>import process</b> before adding the key, this way if the user/dev <b>does not</b> have the key they can see <b>where to get it</b>.') . '</p>'; } } else { $keyNotice = '<h1>' . JText::_('This package has no key.') . '</h1>'; $ownerDetails = '<p>' . JText::_('That means anyone who has this package can install it into JCB. To add an export key simply open the component, go to the tab called <b>settings</b>, bottom right there is a field called <b>Export Key</b>.') . '</p>'; } // Redirect to the list screen with success. $message = array(); $message[] = '<h1>' . JText::_('Export Completed!') . '</h1>'; $message[] = '<p>' . JText::sprintf('Path to the zipped package is: <code>%s</code> <br />%s %s', $model->zipPath, $keyNotice, $ownerDetails) . '</p>'; $this->setRedirect(JRoute::_('index.php?option=com_[[[component]]]&view=joomla_components', false), implode('', $message), 'Success'); return; } else { if ([[[component]]]Helper::checkString($model->packagePath)) { // clear all if not successful [[[Component]]]Helper::removeFolder($model->packagePath); } if ([[[component]]]Helper::checkString($model->zipPath)) { // clear all if not successful JFile::delete($model->zipPath); } } } // Redirect to the list screen with error. $message = JText::_('Export failed, please try again latter!'); $this->setRedirect(JRoute::_('index.php?option=com_[[[component]]]&view=joomla_components', false), $message, 'error'); return; } ``` > cloner ```php public function cloner() { // Check for request forgeries JSession::checkToken() or die(JText::_('JINVALID_TOKEN')); // Get the model $model = $this->getModel('Joomla_components'); // check if export is allowed for this user. $model->user = JFactory::getUser(); if ($model->user->authorise('joomla_component.clone', 'com_[[[component]]]') && $model->user->authorise('core.copy', 'com_[[[component]]]')) { // Get the input $input = JFactory::getApplication()->input; $pks = $input->post->get('cid', array(), 'array'); // Sanitize the input JArrayHelper::toInteger($pks); // check if there is any selections if (![[[Component]]]Helper::checkArray($pks)) { // Redirect to the list screen with error. $message = JText::_('No component was selected, please make a selection of one component and try again!'); $this->setRedirect(JRoute::_('index.php?option=com_[[[component]]]&view=joomla_components', false), $message, 'error'); return; } // only one component allowed at this time elseif (count( (array) $pks) !== 1) { // Redirect to the list screen with error. $message = JText::_('Only one component can be cloned at a time, please select one and try again!'); $this->setRedirect(JRoute::_('index.php?option=com_[[[component]]]&view=joomla_components', false), $message, 'error'); return; } // set the active type $model->activeType = 'clone'; // clone the component and the views and the fields... everything linked to the component. if ($model->cloner($pks)) { // clone was successful $message = JText::_('The Component with all linked admin views, fields linked to admin views, custom admin views, site views, templates and layouts were cloned successful!'); $this->setRedirect(JRoute::_('index.php?option=com_[[[component]]]&view=joomla_components', false), $message); return; } // Redirect to the list screen with error. $message = JText::_('Clone failed!'); $this->setRedirect(JRoute::_('index.php?option=com_[[[component]]]&view=joomla_components', false), $message, 'error'); return; } // Redirect to the list screen with error. $message = JText::_('You do not have permission to clone a component, please contact your system administrator for more help.'); $this->setRedirect(JRoute::_('index.php?option=com_[[[component]]]&view=joomla_components', false), $message, 'error'); return; } ``` Be sure to add it to the right controller: **PHP List view (controller methods)** ![image](/attachments/d23c36c3-47b5-4a74-8b63-42f03d656c71) The normal controller code for function only: ![image](/attachments/86854df6-f3d9-4a59-9914-10870bffbe16) ```php public function getSnippets() { // Check for request forgeries JSession::checkToken() or die(JText::_('JINVALID_TOKEN')); // redirect to the import snippets custom admin view $this->setRedirect(JRoute::_('index.php?option=com_[[[component]]]&view=get_snippets', false)); return; } ``` Be sure to add it to the right controller: **PHP List view (controller methods)** ![image](/attachments/8c56c9a2-7384-428c-b4df-fcf17aeeddae) As you mabe noticed all these are list view buttons, the `single`, and `both` target options as minor changes in the GUI, but the controller side also works with a post method, and can look like this: ```php public function requestTrustKey() { // Check for request forgeries. JSession::checkToken() or jexit(JText::_('JINVALID_TOKEN')); // get the data $originalData = $this->input->post->get('jform', array(), 'array'); if (isset($originalData['id']) && $originalData['id'] > 0 && (isset($originalData['url']) && [[[Component]]]Helper::checkString($originalData['url'])) || (isset($originalData['ip']) && [[[Component]]]Helper::checkString($originalData['ip']))) { // get the model $model = $this->getModel('[[[arg0]]]'); // get the user object if (![[[Component]]]Helper::checkObject($model->user)) { $model->user = JFactory::getUser(); } if ($model->user->authorise('[[[arg0]]].requestTrustKey', 'com_[[[component]]]') && $model->requestTrustKey($originalData)) { // Redirect to the list screen with success. $message = JText::sprintf('The trust key has been requested and will be emailed to you (%s) once the trusted user on the remote sync system (%s) has confirmed to allow this request.', $model->user->email, $originalData['name']); $this->setRedirect(JRoute::_('index.php?option=com_[[[component]]]&view=[[[arg0]]]&layout=edit&id=' . (int) $originalData['id'], false), $message, 'success'); return; } } $this->setError(JText::sprintf('The trust key request to %s has failed!', $originalData['name'])); $this->setMessage($this->getError(), 'error'); // Redirect back to the list screen. $this->setRedirect(JRoute::_('index.php?option=com_[[[component]]]&view=[[[arg0]]]&layout=edit&id=' . (int) $originalData['id'], false)); return false; } ``` Be sure to add it to the right controller: **PHP (controller methods)** You will see in most of these controller methods, we validate access, and then call the module... which is basicly how any post should behave. Anyway to me the actual issue is you need to know how to build your PHP scripting side, and be able to debug this untill it does what you want. What is true is I use this feature in a thousand ways and they have never given me any errors...
Author
Member

THANK YOU!!!!
That was a great explanation and helped a TON.

Although the problem was that I did not PHP List View (controller methods) and was just putting it in the first controller method box.
Once moved, and with your sample code here to get the id values, it all worked great.

THANKS!

THANK YOU!!!! That was a great explanation and helped a TON. Although the problem was that I did not PHP List View (controller methods) and was just putting it in the first controller method box. Once moved, and with your sample code here to get the id values, it all worked great. THANKS!
Sign in to join this conversation.
No Milestone
No project
No Assignees
2 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: joomla/Component-Builder#939
No description provided.