Building a custom adminhtml module. Im using a simple form. it looks like this :
<?php
class Namespace_Modulename_Block_Adminhtml_Modulename_Edit_Tab_Form extends Mage_Adminhtml_Block_Widget_Form
{
protected function _prepareForm()
{
$form = new Varien_Data_Form();
$this->setForm($form);
$fieldset = $form->addFieldset('modulename_form',array('legend'=>Mage::helper('modulename')->__('Module Data')));
$fieldset->addField('test', 'text', array(
'label' => Mage::helper('modulename')->__('Test'),
'name' => 'test',
));
// I want to add a custom button here. Say an action called "Add Option".
//Clicking this action adds input boxes or dropdowns to the form that are to
//to be included in the post when submitting the form (obviously).
I have been looking for a solution on Stack overflow and have not been able to find something that could help. I have then tried searching for something similar present in the Mage Core.
In the admin panel, if i go to [Catalog->Attributes->Manage Attributes] and click on a standard attribute like "Manufacturer" for example, on the second tab, called "Manage labels/Options" i see the following screen :
There is a button and action which allows me to add options (in the form of textboxes) to the form. Identifying this as something i am trying to replicate, i went into the core to try and figure out what to do. If i open the following files (Magento Enteprise 12.0.2) :
Mage_Adminhtml_Block_Catalog_Product_Attribute_Edit_Tab_Options
I see it is empty, but extends Mage_Eav_Block_Adminhtml_Attribute_Edit_Options_Abstract
and I have gone through this file, but little makes sense to me. Am I going down the wrong way? How can I achieve something similar, a button and action which adds fields to a admin form?
thanks
You can set custom template for your form, and add there any tags (like button) via html.
You can add renderer for you field
$customField = $fieldset->addField('test', 'text', array(
'label' => Mage::helper('modulename')->__('Test'),
'name' => 'test',
));
$customField->setRenderer($this->getLayout()->createBlock('yuormodule/adminhtml_yourform_edit_renderer_button'));
in Block Class
class Yournamespace_Yourmodule_Block_Adminhtml_Yourform_Edit_Renderer_Button extends Mage_Adminhtml_Block_Abstract implements Varien_Data_Form_Element_Renderer_Interface {
public function render(Varien_Data_Form_Element_Abstract $element) {
//You can write html for your button here
$html = '<button></button>';
return $html;
}
}
1- First you must create a container for your Grid like this
public function __construct() {
parent::__construct();
$this->setTemplate('markavip/dataflow/edit/form/mapping.phtml');
}
2- in the Same Container you will add your buttons like
public function _prepareLayout() {
$this->setChild('add_button', $this->getLayout()->createBlock('adminhtml/widget_button')
->setData(array(
'label' => Mage::helper('markavip_dataflow')->__('Add Option'),
'class' => 'add',
'id' => 'add_new_option_button'
)));
$this->setChild('delete_button', $this->getLayout()->createBlock('adminhtml/widget_button')
->setData(array(
'label' => Mage::helper('markavip_dataflow')->__('Delete'),
'class' => 'delete delete-option'
)));
return parent::_prepareLayout();
}
public function getAddNewButtonHtml() {
return $this->getChildHtml('add_button');
}
public function getDeleteButtonHtml() {
return $this->getChildHtml('delete_button');
}
3- and in the PHTML you will add thes buttons like this :
<?php echo $this->getAddNewButtonHtml() ?>
and after this you will play in JS functions for add and hide
Related
I use a GridField in SilverStripe to create HTML section items that are rendered on a page. This works so far but it always displays the sections in the order that I added them to the CMS or rather by the ID it gets when it's created.
So my question is: How can i change that order. I don't want to manually change the IDs but would rather do a simple drag and drop.
Edit: Could the use of Elemental be a solution to this problem?
Screenshot of the CMS view
The Page:
class HomePage extends Page
{
private static $has_many = [
'SectionObjects' => SectionObject::class
];
public function getCMSFields()
{
$fields = parent::getCMSFields();
$fields->addFieldToTab('Root.Sections',
$grid = GridField::create('SectionObjects', 'Sections', $this->SectionObjects(), GridFieldConfig_RecordEditor::create())
);
$config = $grid->getConfig();
$dataColumns = $config->getComponentByType(GridFieldDataColumns::class);
$dataColumns->setDisplayFields([
'ID' => 'ID',
'LastEdited' => 'Changed'
]);
return $fields;
}
}
The Section object
class SectionObject extends DataObject{
private static $db = [
'Content' => 'Text',
'BgColor' => Color::class
];
private static $has_one = [
'HomePage' => HomePage::class
];
public function getCMSFields(){
return new FieldList(
TextareaField::create('Content','SectionContent'),
ColorField::create('BgColor', 'Hintergrundfarbe')
);
}
}
Definitely using the elemental module would allow you to have the kind of behaviour you want - but if you don't want to go to the effort of refactoring your code to comply with that module, you could use either the gridfieldextensions or sortablegridfield module to allow you to sort the gridfield (with drag and drop) to your heart's content.
I was looking for a answer, but i never found something. .
My starting Position:
This classes / files are included
Controller, DataDFu1Controller.php
Form DataAPatientType.php ,DataDFu1Type.php
Entity DataAPatient, DataDFu1
views/DataDfu1/ form.html.twig
The DataDFu1Controller contains (to the overview) the indexAction, newAction and
editAction and so on.
Both Formtypes (DataAPatientType.php ,DataDFu1Type.php) comes in one Form (look Method) this form goes to be rendered later in the form.html.twig file for the newAction and the editAction
For the newAction i did it so:
private function createNewForm(DataAPatient $entity)
{
$form = $this->createForm($this->get('data_livebundle.form.dataapatienttype'), $entity, array(
'action' => $this->generateUrl('dataapatient_new'),
'method' => 'POST',
));
return $form->add('dFu1', new DataDFu1Type());
}
later the form comes rendered. . .
So first i create "DataAPatientType.php" Form and then i add the "DataDFu1Type.php" to the form.
In the view -> form.html.twig it looks like that.
for DataDFu1Type:
{{ form_widget(form.dFu1.fu1Examiner1)}}
for DataAPatientType:
{{ form_label(form.pSnnid, 'SNN-ID (if known)', {'label_attr':{'style':'margin-top:3px'}})}}
So i can get a variable or a function with the suffix 'dfu1' after the form.
Everything works so fine. I hope the condition are understandible till now..
Now my Problem:
I have to create also an editAction which opend of course the same view-> form.html.twig with the filled values from a dataset (entity). In this process i don't understand how i can create the Form Object based also (DataAPatientType, DataDFu1Type) with the corresponding data. -> I'm trying to be more specific
private function createEditForm(DataDFu1 $entity)
{ /*
* This function shoud create the editform which insists
* DataAPatientType.php ,DataDFu1Type.php included the data from
* $entity. I have the opportunity to get the entity for DataDFu1Type
* easy directly with the Primary Key and the data for DataAPatientType
* over a Foreign Key which is safed in the $entity
*
*/
}
So i only dont understand how i can create a Form based on two types (DataAPatientType.php ,DataDFu1Type.php) with the corresponding Data inside, that i can render it like in the newAction.
For one Form i did it everytime like so and it works.. but for two types i tried a lot things which didnt worked. Have somebody a experiance? or a Solution for this Problem?
the syntax of the form.html.twig isnt changeable so the form has to be rendered equivalent like in the newAction
Example for creating a form based only on one Type and not two
private function createEditForm(Event $entity)
{
$form = $this->createForm($this->get('qcycle_eventbundle.form.eventtype'), $entity, array(
'action' => $this->generateUrl('event_edit', array('id' => $entity->getId())),
'method' => 'POST'
));
$form->add('preview', 'button', array('label' => 'Preview', 'attr' => array('data-preview' => 'preview')))
->add('submit', 'submit', array('label' => 'Save Changes'))
->add('sendAndSave', 'submit', array('label' => 'Send Mail & Save'));
return $form;
}
i really hope, that my problem and Question understandable
thanks
mjh
If i understand you have this form:
class DataAPatientType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('dFu1', new DataDFu1Type());
$builder->add('pSnnid', 'text');
[...]
}
}
Then in create
$form = $this->createForm(DataAPatientType(), new DataAPatient());
And in edit you can simply do something like
private function createEditForm(DataDFu1 $entity)
{
$form = $this->createForm(DataAPatientType(), entity); //here the form will be "populated" by the entity data
So if you want to set some default value or overwrite an existing value for example, you would use
private function createEditForm(DataDFu1 $entity)
{
entity->setPSnnid('whatever')
$form = $this->createForm(DataAPatientType(), entity); //here the form will be "populated" by the entity data
I have implemented the clone action just like in the documentation. How can I limit the access to the clone action to the user who created the object?
I have already an access denied exception check in my action, but how can I now hide the button in the list view if the user is not the author of that object. The user should still be able to list the order and display it.
This is my route:
protected function configureRoutes(RouteCollection $collection)
{
$collection->add('clone', $this->getRouterIdParameter().'/clone');
}
And my list fields:
protected function configureListFields(ListMapper $listMapper)
{
$listMapper
->add('_action', 'actions', array(
'actions' => array(
'show' => array(),
'edit' => array(),
'clone' => array(
'template' => 'AppBundle:Sonata/Button:clone_button.html.twig'
),
), 'label' => 'Actions'
))
;
}
and my clone action:
public function cloneAction($id = null)
{
$object = $this->admin->getSubject();
if (!$object) {
throw new NotFoundHttpException(sprintf('Unable to find the object with id : %s', $id));
}
If (!$object->isAuthor($this->getUser())) {
throw new AccessDeniedException();
}
$clonedObject = clone $object;
$this->admin->create($clonedObject);
$this->addFlash('sonata_flash_success', 'Cloned successfully');
return new RedirectResponse($this->admin->generateUrl('edit', array('id' => $clonedObject->getId())));
}
As you can see in my clone action, I have a check to see if the user is the author of the order. But how can I remove the button in the list completely by check my isAuthor function?
Because now the user can see the button but if he is unauthorized to clone the order and he clicks the button he get an access denied exception. So I don't want to show the button at all. The same counts for the edit button.
I have thought of something like this:
protected function configureRoutes(RouteCollection $collection)
{
$user = $this->getConfigurationPool()->getContainer()->get('security.token_storage')
->getToken()->getUser();
If (!$object->isAuthor($user)) {
$collection->remove('edit');
$collection->remove('clone');
}
}
But apparently this can't be done.
Does anybody have an idea how to do this?
I would create a Symfony Voter and remove the check from the action. The check would be done, in the voter outside the action, and could be done from anywhere, including the template. You should check the template, it probably already does the check.
Also, off-topic pro-tip, always provide a message inside your exceptions.
throw new AccessDeniedException('Not an author of this object');
I want to add a download button for each row in moduleadmincontroller helper.
I tried to add it by using the following code on RenderList function. But it is not working.
$this->addRowAction('download');
Kindly let me know if I can add custom action for each row and how to process it.
as you know the actions is the default array that have default value array('view', 'edit', 'delete', 'duplicate'); and you can use this but if you want add new action you should use some function.for example you can go to your_prestashop/controllers/admin/AdminRequestSqlController.php
this class add new action with 'export' name
$this->addRowAction('export');
then for create link for this action it is using the displayExportLink() function as you can see in bellow code
public function displayExportLink($token, $id)
{
$tpl = $this->createTemplate('list_action_export.tpl');
$tpl->assign(array(
'href' => self::$currentIndex.'&token='.$this->token.'&
'.$this->identifier.'='.$id.'&export'.$this->table.'=1',
'action' => $this->l('Export')
));
return $tpl->fetch();
}
and then you can get your new action with the initProcess() function or initcontent() function and do something lik download
public function initProcess()
{
parent::initProcess();
if (Tools::getValue('export'.$this->table))
{
$this->display = 'export';
$this->action = 'export';
}
}
I need help creating an if statement that will print out a cancel button only in forms for nodes. Without an if statement the cancel button prints out on all forms, including site search forms. I tried using a '$form_id !=' but adding every form ID where I don't want the cancel button to be doesn't seem very intuitive. Any help would be much appreciated.
<?php
/**
* Implements hook_form_alter().
*/
function cancel_button_form_alter(&$form, &$form_state, $form_id) {
// Here is where I'm having trouble. What variable can
// I put that targets ANY content type?
if ($form_id != 'search_block_form') {
// Add a cancel button.
$form['actions']['cancel'] = array(
'#type' => 'submit',
'#value' => t('Cancel'),
'#access' => TRUE,
'#weight' => 15,
'#submit' => array('cancel_button_form_cancel', 'node_form_submit_build_node'),
'#limit_validation_errors' => array(),
);
}
}
/**
* Custom cancel button callback.
*/
function cancel_button_form_cancel($form, &$form_state) {
$url = $_GET['destination'] ? $_GET['destination'] : '';
drupal_goto($url);
}
If you are in a node/content, the $form variable will have a node object. But if the form is not for/from node then it won't have the node object. You can check like this:
if(isset($form['#node'])) {
// Your code goes here
}
Actually, I have a bit confusion about $form['#node'] (:P). You can get it by debugging the $form or $form_state variable.