0

I have a Drupal 7 custom module form that uploads then processes multiple images.

I have an ajax callback that updates page content with the processed images. BUT, the page update happens when all uploaded files are finished and I would like to alter this to update when each uploaded file is processed.

Form creation:

function awsImage_form($form, &$form_state) {
  $form = [];

  $form['file'] = [
    '#type' => 'file',
    '#name' => 'files[]',
    '#title' => t('Upload files to test'),
    '#description' => t('TIFF\'s, JPG\'s, GIF\'s, and PNG\'s only, 20MB Max Size'),
    '#attributes' => ['multiple' => 'multiple'],
  ];

  $form['submit'] = [
    '#type' => 'submit',
    '#value' => t('Upload'),
    '#ajax' => [
      'callback' => 'awsImage_form_callback',
      'wrapper' => 'results',
      'method' => 'append',
    ],
  ];
  $form['results'] = [
    '#markup' => '<div id="results">Results:</div>',
  ];
  return $form;
}

Form validation [error checking & save location [$destination] removed to keep it brief]:

function awsImage_form_validate($form, &$form_state) {
  //Save multiple files
  $num_files = count($_FILES['files']['name']);

  for ($i = 0; $i < $num_files; $i++) {
    $file = file_save_upload($i, [
      'file_validate_extensions' => ['tif tiff png gif jpg jpeg'],
    ]);
    if ($file) {
      file_prepare_directory($destination, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
      if ($file = file_move($file, $destination, FILE_EXISTS_REPLACE)) {
        $form_state['values']['file'][$i] = $file;
      }
    }
  }
}

AJAX callback [image processing removed to keep it brief]:

function awsImage_form_callback($form, $form_state) {
  $form_state['rebuild'] = TRUE;
  $processed_out = '';
  if (is_array($form_state['values']['file'])) {    
    foreach ($form_state['values']['file'] as $file) {
     // image processing stuff which builds and array: $img_reponse_details
      $output = '';
      foreach ($img_reponse_details as $key => $img_details) {
        // build front end $output
      }
      $processed_out .= $output;
  }
  // push output back to form.
  $element = $form['results'];
  $element['#markup'] = $processed_out;
  return $element;
}

Ideally, I would like to update the front end in the foreach loop in the ajax callback:

function awsImage_form_callback($form, $form_state) {    
  foreach ($form_state['values']['file'] as $file) {
      // deal with each image
      // push output back to form.
      $element = $form['results'];
      $element['#markup'] = $processed_out;
      return $element;
    }
}

But as soon as I return the current result, that's the loop over and only the first file is displayed - which is correct as it is a return... but what I am really after is 'do the AJAX update and carry on with the rest' and I am not sure how to go about that part?

0

The AJAX updates I wanted was to provide some progress updates which I found out I can achieve form api #ajax['progress']. I guess if I had mentioned that in the original question it would have made life a bit simpler :) :

first create a url for the ajax progress update to call:

function awsImage_menu() {
  $items = [];

  $items['path/to/page'] = [
    'title' => t('Import progress'),
    'page callback' => 'import_progress',
    'access callback' => 'user_access',
    'access arguments' => ['access awsImage'],
    'type' => MENU_CALLBACK,
  ];

  return $items;
}

then update the form to use the progress path created in the hook_menu - note the type MUST be 'bar' and the url MUST have the starting forward slash

function awsImage_form($form, &$form_state) {
.....

  $form['submit'] = [
    '#type' => 'submit',
    '#value' => t('Upload'),
    '#ajax' => [
      'callback' => 'awsImage_form_callback',
      'wrapper' => 'results',
      'method' => 'append',
      'progress' => [
        'type' => 'bar',
        'url' => '/path/to/page',
        'interval' => 1000,
      ],
    ],
  ];
....

}

then create your progress function. In my original awsImage_form_callback function I created and updated a session variable: $_SESSION['awsImage']['import_status']

function import_progress() {
  // set up default progress status message
  $progress = [
    'message' => t('Starting import...'),
    'percentage' => -1,
  ];

  // check if there is any status data

    $completed = (isset($_SESSION['awsImage']['import_status'])) ? $_SESSION['awsImage']['import_status'] : false; // calculate progress percentage here

    if ($completed) {
      // there is status data, so build the status message
      $completed = unserialize($completed);
      $total_files = $completed['total'];
      $current_file = $completed['current'];
      $current_file_name = $completed['current_filename'];
      $completed_percentage = ($current_file / $total_files) * 100;

      $progress['message'] = t('Processing:' . $current_file_name);
      $progress['percentage'] = (int) $completed_percentage;
    }

  drupal_json_output($progress);
}

Now when I upload & process multiple files, I get an update every second on which file I am uploading and how much there is left to go... Most of the time, sometimes the progress bar does not do any updates then just shows 100% and completes... but that may be down to local files uploading to a local server and there is not enough time!

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.