5

I have several content types on a site and most of them are user-editable but not user-creatable. However, one content type, Image, allows users to upload a picture. Sometimes, however, users upload the wrong picture and they panic because they don't want that picture to be submitted to the site.

Of course they could always hit the browser's back button, but I'd like to add a Cancel button for additional assurance.

I found this thread that gives various solutions for adding cancel buttons on Drupal.org as well as this feature request for adding a cancel button to D8. However, there are several different general approaches and I don't know how to limit one to a given content type (in this case, my_image).

10

I would use the following code.

function mymodule_form_my_image_node_form_alter(&$form, &$form_state) {
  // If the node is being created, redirect the users to the page where
  // they can choose the content type for a new node; otherwise, redirect
  // them to the page showing the node.
  $destination = (!isset($node->nid) || isset($node->is_new)) ? 'node/add' : "node/{$node->nid}";

  $form['actions']['cancel'] = array(
    '#markup' => l(t('Cancel'), $destination),
    '#weight' => 20,
  );
}  

Notes

  • markup is the default form element type; there is no need to use #type = 'markup'.

  • In the node edit form, all the buttons are inside $form['actions']. The weights used from the buttons that form is using are 5 (Save), 10 (Preview), and 15 (Delete). I used 20 to show the link after those buttons, but you can change its weight to show it in a different position.

    // Add the buttons.
    $form['actions'] = array('#type' => 'actions');
    $form['actions']['submit'] = array(
      '#type' => 'submit', 
      '#access' => variable_get('node_preview_' . $node->type, DRUPAL_OPTIONAL) != DRUPAL_REQUIRED || (!form_get_errors() && isset($form_state['node_preview'])), 
      '#value' => t('Save'), 
      '#weight' => 5, 
      '#submit' => array('node_form_submit'),
    );
    $form['actions']['preview'] = array(
      '#access' => variable_get('node_preview_' . $node->type, DRUPAL_OPTIONAL) != DRUPAL_DISABLED, 
      '#type' => 'submit', 
      '#value' => t('Preview'), 
      '#weight' => 10, 
      '#submit' => array('node_form_build_preview'),
    );
    if (!empty($node->nid) && node_access('delete', $node)) {
      $form['actions']['delete'] = array(
        '#type' => 'submit', 
        '#value' => t('Delete'), 
        '#weight' => 15, 
        '#submit' => array('node_form_delete_submit'),
      );
    }
    
  • There isn't the need to use arg() to understand if the form is for editing an existing node, or for creating a new one. I checked !isset($node->nid) || isset($node->is_new) is TRUE, to verify the node is being created. Drupal seems to simply check empty($node->nid), even though node_object_prepare() uses !isset($node->nid) || isset($node->is_new).

  • If $_GET['destination'] is set, the link added with l() will point to that path.

1
  • should we have to add the details in the hook_menu() when altering a node form(form adding a content type)
    – Kalanka
    Nov 6 '17 at 8:32
7

All those solutions are for adding Cancel link not a button!

If you want to add the button instead of a link you should use this code for module cancel_button.module:

function cancel_button_form_alter(&$form, &$form_state, $form_id) {
    // node types for which Cancel button should be added
    $types = array(
      'biography',
      'faq',
      'draftlaw',
      'vocabulary',
      'draftlaw_page',
      'encyclopedia',
      'user', // if you want to add user's edit form too
    );

    foreach($types as $type) {
        if($type.'_node_form' == $form_id || $type.'_profile_form' == $form_id) {
            $form['actions']['cancel'] = array(
                '#type'   => 'submit',
                '#value'  => t('Cancel'),
                '#access' => TRUE,
                '#weight' => 55,
                '#submit' => array('cancel_button_form_cancel', 'node_form_submit_build_node'),
                '#limit_validation_errors' => array(),
                );
            }
        }
    }

    /**
     * cancel_button callback.
     */
    function cancel_button_form_cancel($form, &$form_state) {
        $link = '';
        $path = 'node';
        if('user_profile_form' == $form['#form_id']){
            $path = 'user';
        }
        if(arg(1) == 'add') {
            // If adding entity link to [user|node]/add screen
            $link = $path . '/add';
        }
        elseif(arg(2) == 'edit') {
            // If editing entity, link to node view screen
            $link = $path . '/' . arg(1);
        }
        $url = $_GET['destination'] ? $_GET['destination'] : $link;
        drupal_goto($url);
    }
5
  • The link was good enough for me, because I am using CSS to style my buttons anyway, and the link can be styled to look exactly the same way. Jan 19 '13 at 1:50
  • Good. My problem was the same, but with a button. So, maybe someone else likes my solution. Jan 23 '13 at 6:34
  • what's with the node_form_submit_build_node? May 6 '14 at 0:48
  • 3
    Arguably, as "Cancel" is an action with no side effects (it's "idempotent") it should really be a link and not a button. Buttons (certainly in Drupal) typically submit forms via POST, which is intended for non-idempotent requests, so even if the end result is to do nothing they still fire up the server, defeat caches, add to logs etc. Links can always be styled as buttons for consistency.
    – J-P
    Oct 29 '15 at 9:42
  • Sounds reasonable! Oct 30 '15 at 5:42
7
+50

Each node form has its own unique id. It's consisted of node type's machine name followed by _node_form. So your my_image node type's form id is my_image_node_form

Here's a mock untested function (D6) to illustrate how to implement:

function HOOK_form_alter(&$form, &$form_state, $form_id) {
  $types = array(
    'my_image',
  );

  foreach($types as $type) {
    if($type.'_node_form' == $form_id) {
      if(arg(1) == 'add') {
        // If adding node link to node/add screen
        $link = l(t('Cancel'), 'node/add');
      }
      elseif(arg(2) == 'edit') {
        // If editing node, link to node view screen
        $link = l(t('Cancel'), 'node/'.arg(1));
      }
      $form['cancel'] = array(
        '#type' => 'markup',
        '#value' => $link,
        '#weight' => 0,
      );
    }
  }
}

If using Drupal 7 then change #value on the array to #markup.

2
  • I added this function to my custom module, replaced HOOK with my module's name, changed my_image to the machine name of my content type, saved, and cleared the cache twice, but, although Drupal outputs no error, I also don't get a cancel button. I know you said it's untested code, but I'm unable to figure out what's wrong with this. Jun 11 '12 at 10:43
  • Are you using Drupal 6 or 7? If Drupal 7 then change #value on the array to #markup
    – Beebee
    Jun 11 '12 at 12:27
1
function mymodule_form_my_image_node_form_alter(&$form, &$form_state) {
  // If the node is being created, redirect the users to the page where
  // they can choose the content type for a new node; otherwise, redirect
  // them to the page showing the node.
  $destination = (!isset($node->nid) || isset($node->is_new)) ? 'node/add' : "node/{$node->nid}";

  $form['actions']['cancel'] = array(
    '#markup' => l(t('Cancel'), $destination),
    '#weight' => 20,
  );
}  
0

There's a module for that!: http://drupal.org/project/nodeformsettings ... and this module is really fantastic. It's worth to check out.

-1

You can do that using the hook form alter:

function myModule_form_alter($form_state,$form_id){
 if($form_id=='myFormId'){
  // add the cancell button to your form
 }
}

Give that a shot.

1
  • 1
    This answer is too generic; the OP is asking how to alter the edit form for a node of a specific content type.
    – apaderno
    May 18 '12 at 11:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.