I'm trying to add an image upload to the theme settings in a theme I made. The image upload field does appear, however nothing actually gets uploaded and the image never get's saved.

in theme-settings.php I have the function

function mytheme_form_system_theme_settings_alter(&$form, $form_state, $form_id = NULL) {
    $form['banner']['banner_image'] = array(
        '#type' => 'file',
        '#title' => t('Banner Image'),
        '#description' => t('Upload a banner image'),
        '#default_value' => theme_get_setting('banner_image'),
    );
}

The image upload field does appear, but when choosing the file and clicking 'save configuration' nothing actually gets saved.

up vote 2 down vote accepted

The problem with your code is that it needs a submission handler to handle the uploaded file. Using #file as form element type is fine, since that is also done from Drupal core in system_theme_settings().

$form['logo']['settings']['logo_path'] = array(
  '#type' => 'textfield',
  '#title' => t('Path to custom logo'),
  '#description' => t('The path to the file you would like to use as your logo file instead of the default logo.'),
  '#default_value' => theme_get_setting('logo_path', $key),
);
$form['logo']['settings']['logo_upload'] = array(
  '#type' => 'file',
  '#title' => t('Upload logo image'),
  '#maxlength' => 40,
  '#description' => t("If you don't have direct file access to the server, use this field to upload your logo.")
);

The form submission handler (system_theme_settings_submit()) then handles the uploaded file with the following code.

  // Exclude unnecessary elements before saving.
  form_state_values_clean($form_state);

  $values = $form_state['values'];

  // Extract the name of the theme from the submitted form values, then remove
  // it from the array so that it is not saved as part of the variable.
  $key = $values['var'];
  unset($values['var']);

  // If the user uploaded a new logo or favicon, save it to a permanent location
  // and use it in place of the default theme-provided file.
  if (!empty($values['logo_upload'])) {
    $file = $values['logo_upload'];
    unset($values['logo_upload']);
    $filename = file_unmanaged_copy($file->uri);
    $values['default_logo'] = 0;
    $values['logo_path'] = $filename;
    $values['toggle_logo'] = 1;
  }

You could use similar code in a form submission handler, but as described in When theme-settings.php has a managed_file field and a submit callback, " Call to undefined function" error is thrown. such form aubmission handler would not be found because Drupal doesn't load the theme-settings.php file automatically.
If you get around that, and you are able to make Drupal automatically load the file containing the submission handler, you could use the following code in your submission handler.

  if (!empty($form_state['values']['banner_image'])) {
    $file = $form_state['values']['banner_image'];
    unset($form_state['values']['banner_image']);
    $filename = file_unmanaged_copy($file->uri);
    $form_state['values']['banner_image_path'] = $filename;
  }

With theme_get_setting('banner_image_path') you will then get the path of the last uploaded file.
I would do as Drupal does, and add a textfield for the path of the last uploaded file.

  • Hmm where you manually set the path with text works great but I'm not having any look with the upload. I can browse for a file and save the configuration but nothing actually gets uploaded here is the image of the form and the code. image: imgur.com/DDNrze1 code: pastebin.com/WrVa81CC – Matt Dec 16 '15 at 14:31
  • The part you are missing is the one that allows Drupal to automatically include the file containing the form submission handler; I left that out because you need to check the issue I linked and see which approach works for you. – kiamlaluno Dec 16 '15 at 15:28
  • Keep in mind that the code shown in the issue is for a module; probably you need to adapt it, since you are developing a theme. – kiamlaluno Dec 16 '15 at 15:29

A file element just gets the data to your server - you need to do something with it if you want that data to persist.

Drupal has you covered with the managed_file element type, though:

$form['banner']['banner_image'] = array(
    '#type' => 'managed_file',
    '#title' => t('Banner Image'),
    '#description' => t('Upload a banner image'),
    '#default_value' => theme_get_setting('banner_image'),
    '#destination' => 'public://foo/bar',
);

Note that managed files are marked as temporary by default (so that orphan files can eventually be deleted if the associated form isn't actually submitted etc), so there's a bit of work to do in a submit handler:

$file = file_load($form_state['values']['banner_image']);
$file->status = FILE_STATUS_PERMANENT;
file_save($file);
  • 1
    If I correctly recall, the submission handler is not invoked, in this case. – kiamlaluno Dec 15 '15 at 18:26
  • Ah yeah @kiamlaluno, I knew something was niggling at me about doing this in the theme settings form, that'll be it. I think implementing a hook_form_alter() on that form in the same theme, and adding the submit handler that way, should work – Clive Dec 15 '15 at 18:27

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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