1

I have a custom module that I have created to display 3 different images with a 'vote' beneath each image. The idea is to have the viewer vote on the one they like and it gets ranked against the other 2 images using votingAPI. What I am trying to do on callback is do the voting calculations (which I have), then replace the buttons with the voting percentage for each of the images. Wait 3 seconds and refresh the form with 3 more images.

What I am having difficulty with right now is just redrawing the form after submit, with the same 3 images and the percentage displaying instead of the button.

Here is the code I am using to define the block, block_view, and form. Any suggestions or help would be greatly appreciated.

function steal_or_burn_block_info() {
  $blocks = array();
  $blocks['steal_or_burn'] = array(
    'info' => t('Steal or Burn Homepage Block'),
    'cache' => DRUPAL_NO_CACHE,
  );
return $blocks;
}

function steal_or_burn_block_view($delta = '') {
  $block = array();
  switch($delta) {
    case 'steal_or_burn':
      $block['subject'] = ("Favorite Image");
      $block['content'] = drupal_get_form('steal_or_burn_form');
    }

  return $block;
}


function steal_or_burn_form($form, &$form_state) {

  $has_cookie = get_cookie('stealorburn');

  // Get the previous Steal or Burn NodeId's as an array
  $prev_sob_nids = get_sob_cookie_nids(COOKIE_NAME);

  // Load the NodeId's of the remaining Steal or Burn images in the database
  $unviewed_nids = load_sob_node_ids('stealorburn');

  // Find the difference between the previously viewed NodeId's against the full database
  // return an array of only those that have not yet been viewed
  if($prev_sob_nids) {
    $stealorburn_raw = array_diff($unviewed_nids, $prev_sob_nids);
  } else {
    $stealorburn_raw = $unviewed_nids;
  }
  // Randomize the array of not-yet-viewed Product images
  shuffle($stealorburn_raw);
  // Select the next 3 images to display
  $stealorburn = array_slice($stealorburn_raw, 0,3, TRUE);

  $i = 1;
  foreach($stealorburn as $value) {

  $node = node_load($value);
  $image = field_get_items('node', $node, 'field_brand_image');
  $image_rendered = field_view_value('node', $node, 'field_brand_image', $image[0], array(
    'type' => 'image',
    'settings' => array(
      'image_style' => 'thumbnail',
      ),
  ));
  $brand = $node->title;
  $item_name = field_get_items('node', $node, 'field_item_name');
  $item_id = $node->nid;
  $image_uri      = $image_rendered['#item']['uri'];
  $style          = 'steal_burn_home_image';
  $derivative_uri = image_style_path($style, $image_uri);
  $new_image_url  = file_create_url($derivative_uri);

  switch ($i) {
    case 1:
      $submit_button = 'steal_burn_first';
      break;
    case 2:
      $submit_button = 'steal_burn_second';
      break;
    case 3:
      $submit_button = 'steal_burn_third';
      break;
  }

  // Block output in HTML with div wrapper
  $form['steal_or_burn_form'][]['image'] = array(
      '#prefix' => "<div class='steal_burn_image_block_$i'>",
      '#type' => 'markup',
      '#markup' => "<img src='$new_image_url'>",
    );
  $form['steal_or_burn_form'][]['brand'] = array(
      '#prefix' => '<div class="steal-or-burn-field-title">',
      '#type' => 'markup',
      '#markup' => $brand,
      '#suffix' => '</div>',
    );
  $form['steal_or_burn_form'][]['item_name'] = array(
      '#prefix' => '<div class="steal-or-burn-field-item-name">',
      '#type' => 'markup',
      '#markup' => $item_name[0]['value'],
      '#suffix' => '</div>',
    );
  $form['steal_or_burn_form'][]['product_id'] = array(
      '#type' => 'hidden',
      '#value' => $item_id,
    );
  $form['steal_or_burn_form'][]['submit'] = array(
      '#type' => 'submit',
      '#id' => $submit_button,
      '#name' => $node->nid,
      '#value' => t('Vote'),
      /*'#submit' => array('steal_or_burn_submission_callback'),*/
  );

  $i++;
} // end block image loop

return $form['steal_or_burn_form'];

}

2 Answers 2

0

You would need to have an AJAX callback.

You would need to wrap your submit buttons --- (or your whole form, doesn't matter). Set the 'wrapper' with the ID of that HTML element ; and then set the name of your ajax callback function in 'callback'. Your ajax callback returns the form element (in case you're just wrapping the submit buttons) or the whole form. You shouldn't do any change in the form or its state as mentioned in the documentation. You do the changes in your form builder function. In this case, that will be steal_or_burn_form. But, you can change a form element and then return that.

Read the documentation for more info. Also, this one's a good example of a submit-driven ajax:

Form Builder

Ajax Callback of the form above

1
  • Thank you for your suggestions. I took a queue from the links you provided, re-created my form a little and now have it working. I think parts of it are still a square peg being forced through a round hole, but I'm still tweaking it. Commented Sep 23, 2013 at 18:07
0

After a few more days work tweaking the form, I was able to get it to work by modifying this form and adding a js file for some of the jQuery functionality I just couldn't figure out how to accomplish directly in the form or through helper functions. Here is my updated version of the form:

function steal_or_burn_form($form, &$form_state) {

  get_cookie('stealorburn');

  // Get the previous Steal or Burn NodeId as an offset
  $prev_sob_offset = get_sob_cookie_offset(COOKIE_NAME);

  // Load the NodeId's of the remaining Steal or Burn images in the database
  $unviewed_nids = load_sob_node_ids('stealorburn');

  // Find the index of the last viewed NodeId against the full database
  // return an array of the next 3
  if($prev_sob_offset) {
    // There was an offset saved in the cookie
    if($prev_sob_offset <= (count($unviewed_nids) - 3)) {
      $stealorburn = array_slice($unviewed_nids, ($prev_sob_offset), 3);
      $sob_offset = $prev_sob_offset + 3;
    } else {
      $stealorburn = array_slice($unviewed_nids, 0, 3);
      $sob_offset = 3;
    }
  } else {
      $stealorburn = array_slice($unviewed_nids, 0, 3);
      $sob_offset = 3;
  }

  //set hidden field for the sob_offset so it may be saved during form-submit
  $form['cookie_offset'] = array(
    '#type' => 'hidden',
    '#default_value' => $sob_offset,
  );

  $i = 1;
  foreach($stealorburn as $value) {

    $node = node_load($value, NULL, TRUE);

    $image = field_get_items('node', $node, 'field_brand_image');
    $image_rendered = field_view_value('node', $node, 'field_brand_image', $image[0], array(
      'type' => 'image',
      'settings' => array(
        'image_style' => 'thumbnail',
        ),
    ));
    $brand = $node->title;
    $item_name = field_get_items('node', $node, 'field_item_name');
    $item_id = $node->nid;

    $image_uri      = $image_rendered['#item']['uri'];
    $style          = 'steal_burn_home_image';
    $derivative_uri = image_style_path($style, $image_uri);
    $success        = file_exists($derivative_uri) || image_style_create_derivative($style, $image_uri, $derivative_uri);
    $new_image_url  = file_create_url($derivative_uri);

    switch ($i) {
      case 1:
        $submit_button = 'steal_burn_first';
        break;
      case 2:
        $submit_button = 'steal_burn_second';
        break;
      case 3:
        $submit_button = 'steal_burn_third';
        break;
    }

    // Block output in HTML with div wrapper
    $form['image'.$item_id] = array(
      '#prefix' => "<div class='steal_burn_image_block_$i'>",
      '#type' => 'markup',
      '#markup' => "<img src='$new_image_url'>",
    );
    $form['brand'.$item_id] = array(
      '#prefix' => '<div class="steal-or-burn-field-title">',
      '#type' => 'markup',
      '#markup' => $brand,
      '#suffix' => '</div>',
    );
    $form['item_name'.$item_id] = array(
      '#prefix' => '<div class="steal-or-burn-field-item-name">',
      '#type' => 'markup',
      '#markup' => $item_name[0]['value'],
      '#suffix' => '</div>',
    );
    $form['product_id' . $item_id] = array(
      '#type' => 'hidden',
      '#default_value' => $item_id,
      '#value' => $item_id,
    );

    $form['submit_'.$item_id] = array(
      '#type' => 'submit',
      '#id' => $submit_button,
      '#name' => $item_id,
      '#value' => t('Vote'),
      '#prefix' => '<div id="vote_btn_'.$item_id.'" class="steal-or-burn-vote">',
      '#suffix' => '</div></div>',
      '#ajax' => array(
        'callback' => 'steal_or_burn_ajax_callback',
        'event' => 'click',
        'progress' => array('type' => 'none'),
      ),
    );

    $i++;
  } // end block image loop

  $form['#attached']['js'] = array
  (
    array
    (
      'type'  => 'file',
      'data' => drupal_get_path('module', 'steal_or_burn') . '/steal_or_burn.js',
    ),
  );

  return $form;
}

function steal_or_burn_ajax_callback($form, &$form_state) {
  //function steal_or_burn_submit($form, &$form_state) {

  while (list($key, $value) = each($form_state['values'])) {
    if(substr_count($key, 'product_id') == 1) {
      if(!is_null($value) ) {
        $raw_values[] = $value;
      }
    }
    if($value == 'Vote') {
      $voted = $key;
    }
  }

  for($i = 0, $size = count($raw_values); $i < $size; ++$i) {
    if($raw_values[$i] != $voted) {
      $not_voted[] = $raw_values[$i];
      $votes[$raw_values[$i]]['entity_type'] = 'stealorburn';
      $votes[$raw_values[$i]]['entity_id'] = $raw_values[$i];
      $votes[$raw_values[$i]]['value_type'] = 'points';
      $votes[$raw_values[$i]]['value'] = 0;
    } else {
      $votes[$voted]['entity_type'] = 'stealorburn';
      $votes[$voted]['entity_id'] = $voted;
      $votes[$voted]['value_type'] = 'points';
      $votes[$voted]['value'] = 100;
    }
  }

  // Need to set criteria to be an empty array, otherwise it would reset votes.
  $criteria = array();
  // Add vote data to the database for these 3 images
  $current_vote = votingapi_set_votes($votes, $criteria);

  $submitted_image_nids = array_values($raw_values);

  for($i = 0, $size = count($submitted_image_nids); $i < $size; ++$i) {
    $image_nid = $submitted_image_nids[$i];
    $votes_this_round[] = $current_vote['stealorburn'][$image_nid][2]['value'];
  }

  $total_votes = array_sum($votes_this_round);

  for($i = 0, $size = count($votes_this_round); $i < $size; ++$i) {
    $points[$i] = intval(($votes_this_round[$i] / $total_votes) * 100);
  }
    //Using the $plug variable to make sure the sum of results = 100%
    $plug = 100 - array_sum($points);
    if($plug > 0) {
      $lowest_value = min($points);
    }

    foreach($points as $key => $value)
    {
      if($value == $lowest_value) {
        $points[$key] = $value + $plug;
      }
    }

  $tracking_votes = array_combine($submitted_image_nids, $points);

    $commands = array();
    foreach($tracking_votes as $key => $value) {
      $replace_id = "#vote_btn_$key";
      $commands[] = ajax_command_replace($replace_id, drupal_render($form[$replace_id]));
      $vote_percentages[] = $value . "%";
    }

  // Include a custom function that will be called in the JavaScript file:
  $commands[] = array
  (
    // The command will be used in the JavaScript file
    'command' => 'stealorburnCallback',
    // Pass the form values of each element to the JS function:
    'votePercentages' => $vote_percentages,
  );

  //delete the cookie before writing the new one
  steal_or_burn_cookie_delete('stealorburn');

  $new_sob_offset = $form_state['values']['cookie_offset'];

  //write the cookie with updated index
  $steal_or_burn_to_cookie = $new_sob_offset;
  steal_or_burn_cookie_save('stealorburn', $steal_or_burn_to_cookie);

  // And finally, we will return all of our commands to the system to be executed:
  return array('#type' => 'ajax', '#commands' => $commands);
  }

The contents of the JS file are primitive. Right now the page is getting reloaded after 2 seconds of displaying the results. My next challenge is to make a function or alter the form in a way that I can reload it after the 2-second delay without having to reload the whole page.

**myjsfile.js

(function($, Drupal)
{
    // Our function name is prototyped as part of the Drupal.ajax namespace, adding to the commands:
    Drupal.ajax.prototype.commands.stealorburnCallback = function(ajax, response, status)
    {
        $('.steal-or-burn-field-title').each(function(index, obj){
            if(index == 0) {
                $("<div class='steal-or-burn-percentage'>"+response.votePercentages[0]+"</div>").insertBefore(this);
            } else if (index == 1) {
                $("<div class='steal-or-burn-percentage'>"+response.votePercentages[1]+"</div>").insertBefore(this);
            } else if (index == 2) {
                $("<div class='steal-or-burn-percentage'>"+response.votePercentages[2]+"</div>").insertBefore(this);
            }
        });


        setTimeout(function() {
            location.reload(true);
        }, 2000);

        return false;

    };
}(jQuery, Drupal));

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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