Drupal 7 Bootstrap image field upload widget

When using the Bootstrap theme in Drupal 7, the image upload widget leaves a lot to be desired. I looks pretty crappy
Below is how I overcame this.
It should probably work with file fields too but instead of overriding THEME_image_widget, you would override THEME_file_widget, though I haven't tried it yet.

First, add this to your css

.btn-file {
  position: relative;
  overflow: hidden;
}

.btn-file input[type=file] {
  position: absolute;
  top: 0;
  right: 0;
  min-width: 100%;
  min-height: 100%;
  font-size: 100px;
  text-align: right;
  filter: alpha(opacity = 0);
  opacity: 0;
  outline: none;
  background: white;
  cursor: inherit;
  display: block;
}

Then override the theme_image_widget in your template.php
A note on admin theme
If your upload widget is on a node edit page (or any admin page) the active theme will be your admin theme. This means the code will need to go into your admin themes template.php and the css in your admin themes css.

function MYTHEME_image_widget($variables) {
  $element = $variables['element'];
  $output = '';
  $output .= '<div class="image-widget form-managed-file clearfix">';

  if (isset($element['preview'])) {
    $output .= '<div class="image-preview">';
    $output .= drupal_render($element['preview']);
    $output .= '</div>';
  }

  $output .= '<div class="image-widget-data">';
  if ($element['fid']['#value'] != 0) {
    $element['filename']['#markup'] = '<div class="form-group">' . $element['filename']['#markup'] . ' <span class="file-size badge">' . format_size($element['#file']->filesize) . '</span></div>';
  }
  else {
    $element['upload']['#prefix'] = '<div class="input-group"><span class="input-group-btn"><span class="btn btn-primary btn-file">Browse';
    $element['upload']['#suffix'] = '</span></span>';
    $element['upload_button']['#prefix'] = '<input class="form-control" type="text" readonly=""><span class="input-group-btn">';
    $element['upload_button']['#suffix'] = '</span></div>';
  }
  drupal_add_js("
    (function($) {
      Drupal.behaviors.bootstrapImages = {
        attach: function (context) {
              $('.btn-file :file', context).once().on('change', function() {
                var txt  = '';
                var input = $(this);
                var numFiles = input.get(0).files ? input.get(0).files.length : 1;
                if (numFiles > 1) {
                  txt = numFiles + ' Selected';
                } else {
                  txt = input.val();
                }
                input.closest('.input-group').children('input[type=text]:lt(1)').val(txt);
              });
        }
      };
    })(jQuery)
  ", 'inline');
 
  $output .= drupal_render_children($element);
  $output .= '</div>';
  $output .= '</div>';

  return $output;
}

I should mention that this was inspired by Cory LaViska's blog post HERE

Update:
To get it to work with file fields too, just add:

function MYTHEME_file_widget($variables) {
  return MYTHEME_image_widget($variables);
}

Standard Vs Altered

Comments

Hi,

I cannot get it to work ... Do I missunderstood the idea? I'm trying to get preview of uploaded image.

I've got form like this:

function testform($form, &$form_state) {
  $form = array();
  $form['con_image'] = array(
        '#type' => 'managed_file',
        '#title' => t('Photo'),
        '#required' => TRUE,
        '#default_value' => variable_get('con_image', ''),
        '#progress_indicator' => 'bar',
        '#progress_message' => 'Wait ...',
        '#upload_location' => 'public://gallery/',
        '#theme' => 'testform_image_widget',
        '#upload_validators' => array(
            'file_validate_is_image' => array(),
            'file_validate_extensions' => array('jpg jpeg'),
            'file_validate_image_resolution' => array('6000x4000','800x600'),
            'file_validate_size' => array(6 * 1024 * 1024),
            'default_value' => theme_get_setting('con_image'),
        ),
       
       
  );
 
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Submit'),
  );
  return $form;
}

Than I add your code:

function theme_testform_image_widget($variables) {
  $element = $variables['element'];
  $output = '';
  $output .= '<div class="image-widget form-managed-file clearfix">';

  if (isset($element['preview'])) {
    $output .= '<div class="image-preview">';
    $output .= drupal_render($element['preview']);
    $output .= '</div>';
  }

  $output .= '<div class="image-widget-data">';
  if ($element['fid']['#value'] != 0) {
    $element['filename']['#markup'] = '<div class="form-group">' . $element['filename']['#markup'] . ' <span class="file-size badge">' . format_size($element['#file']->filesize) . '</span></div>';
  }
  else {
    $element['upload']['#prefix'] = '<div class="input-group"><span class="input-group-btn"><span class="btn btn-primary btn-file">Browse';
    $element['upload']['#suffix'] = '</span></span>';
    $element['upload_button']['#prefix'] = '<input class="form-control" type="text" readonly=""><span class="input-group-btn">';
    $element['upload_button']['#suffix'] = '</span></div>';
  }
  drupal_add_js("
    (function($) {
      Drupal.behaviors.bootstrapImages = {
        attach: function (context) {
              $('.btn-file :file', context).once().on('change', function() {
                var txt  = '';
                var input = $(this);
                var numFiles = input.get(0).files ? input.get(0).files.length : 1;
                if (numFiles > 1) {
                  txt = numFiles + ' Selected';
                } else {
                  txt = input.val();
                }
                input.closest('.input-group').children('input[type=text]:lt(1)').val(txt);
              });
        }
      };
    })(jQuery)
  ", 'inline');

  $output .= drupal_render_children($element);
  $output .= '</div>';
  $output .= '</div>';

  return $output;
}

The form is working, the image is added but no luck with preview :(

The code I posted is to override the default theme_image_widget.
Is your theme function even being used? Are you sure you pasted the code in the correct theme (your admin theme if on an admin page). If not you may have to register your custom theme function with HOOK_theme
This is also assuming that the theme on your page is a bootstrap theme.

Totally agree that widget needs work! Any chance of a screenshot so I can what I should be aiming for?

Hi! first thanks for this, it's working great. But it's not being applied to another image upload field on the same page and therefore it looks off. I believe it's the way in which the image upload field is created inside of a separate module.

the module I'm using is the advpoll image field.. which is sandbox and I've had to hack it a bunch already just to get it to work desirably, but I digress.

http://cgit.drupalcode.org/sandbox-xandeadx-1871070/tree/advpoll_field_i...

At the top of this module file is where he adds the image field to advpoll's existing form. I was just wondering if you knew how I could apply this formatting to that image field as well. I don't know what hook to use. Or how to apply this to every image widget? any help is appreciated but thanks no matter what.

Hi Russ,
I think in the first function in the file you linked to you could probably add a #theme item to the array.
like this:

  $element['image'] = array(
    '#type' => 'managed_file',
    '#title' => t('Image'),
    '#default_value' => $default_value ? $default_value : 0,
    // TODO: Do not hardwire image location
    '#upload_location' => 'public://advpoll/advpoll-field-image',
    '#upload_validators' => array(
      'file_validate_is_image' => array(),
      'file_validate_extensions' => array('png gif jpg jpeg'),
    ),
    '#theme' => 'image_widget',
  );

I have not tested this, but give it a try.

You seriously are a life saver. I couldn't find a list of those elements anywhere in order to find that. I guess I just don't know enough about theming widgets yet. Again, thanks a ton. It looks much better now.

Very very GOOD !!!

Many Thanks!

Add new comment

You must have Javascript enabled to use this form.