You are here

uc_option_image.module in Ubercart Option Images 7

Same filename and directory in other branches
  1. 6 uc_option_image.module

Allow store administrators to add images to attribute options.

@author Aaron Craig <aaron@evolving-design.com>

This module is a complete rewrite of the original UC Option Image module by Tj Holowaychuk <tj@vision-media.ca/>. The module has been rewritten to accomodate the many changes in the Drupal API for D7 and the Ubercart API for UC3

File

uc_option_image.module
View source
<?php

/**
 * @file
 * Allow store administrators to add images to attribute options.
 *
 * @author
 * Aaron Craig <aaron@evolving-design.com>
 *
 * This module is a complete rewrite of the original UC Option Image module by Tj Holowaychuk <tj@vision-media.ca/>.
 * The module has been rewritten to accomodate the many changes in the Drupal API for D7 and the Ubercart API for UC3
 */

/**
 * Implements hook_permission().
 */
function uc_option_image_permission() {
  return array(
    'view option images' => array(
      'title' => t('view option images'),
      'description' => t('Allow the user to view option images'),
    ),
    'administer option images' => array(
      'title' => t('administer option images'),
      'description' => t('Allow the user to administer option images'),
    ),
  );
}

/**
 * Implements hook_theme().
 */
function uc_option_image_theme($existing, $type, $theme, $path) {
  return array(
    'uc_option_image_selected' => array(
      'variables' => array(
        'values' => NULL,
        'aid' => NULL,
        'pid' => NULL,
      ),
      'file' => 'uc_option_image.theme.inc',
    ),
    'uc_option_image_inline' => array(
      'variables' => array(
        'values' => NULL,
      ),
      'file' => 'uc_option_image.theme.inc',
    ),
  );
}

/**
 * Implements hook_form_alter().
 */
function uc_option_image_form_alter(&$form, &$form_state, $form_id) {

  // Before rendering the form, add the images
  if (strstr($form_id, 'uc_product_add_to_cart_form')) {
    $form['#pre_render'][] = 'uc_option_image_add_to_cart_prerender';
  }
  else {
    if (strstr($form_id, 'uc_product_kit_add_to_cart_form')) {
      $form['#pre_render'][] = 'uc_option_image_kit_add_to_cart_prerender';
    }
  }

  // Change the AJAX handler for the add to cart form (and the add product form)
  // TODO: some solution for uc_order_add_product_form
  if (strpos($form_id, 'add_to_cart_form')) {
    if (isset($form['attributes'])) {
      foreach ($form['attributes'] as $key => $attribute) {
        if (is_numeric($key) && isset($form['attributes'][$key]['#ajax'])) {
          if ($form['attributes'][$key]['#ajax']['callback'] == 'uc_attribute_option_ajax') {
            $form['attributes'][$key]['#ajax']['callback'] = 'uc_option_image_uc_attribute_option_ajax';
          }
        }
      }
    }
  }
}

// Extended AJAX handler
function uc_option_image_uc_attribute_option_ajax($form, $form_state) {
  $return = uc_attribute_option_ajax($form, $form_state);

  // changes ajax_command_replace item
  $parents = $form_state['triggering_element']['#array_parents'];
  $wrapper = '#' . $form_state['triggering_element']['#ajax']['wrapper'];
  while ($key = array_pop($parents)) {
    if ($key == 'attributes') {
      array_push($parents, $key);
      $element = drupal_array_get_nested_value($form, $parents);

      // _uc_option_image_modify_attributes($element);
      foreach ($return['#commands'] as $key => $command) {
        if ($command['command'] == 'insert' && $command['method'] == 'replaceWith' && $command['selector'] == $wrapper) {
          $option = 0;
          foreach ($element as $aid => $attribute) {
            if (is_numeric($aid)) {
              foreach ($attribute['#options'] as $oid => $text) {
                $oldtitle = $wrapper . " #oldtitle_{$oid}";
                $html = "<span class='oldtitle' id='oldtitle_{$oid}'>{$text}</span>";
                if ($option == 0) {

                  // first replace action replaces the old one
                  $option++;
                  $return['#commands'][$key] = ajax_command_replace($oldtitle, $html);
                }
                else {

                  // subsequent replace actions are added at the end
                  $return['#commands'][] = ajax_command_replace($oldtitle, $html);
                }
              }
              break;
            }
          }
          break;
        }
      }
      break;
    }
  }
  return $return;
}

/**
 * Add the image to each option element.
 */
function uc_option_image_add_to_cart_prerender($element) {
  _uc_option_image_modify_attributes($element);
  return $element;
}
function uc_option_image_kit_add_to_cart_prerender($element) {
  foreach ($element['products'] as $key => $product) {
    if (is_numeric($key)) {
      _uc_option_image_modify_attributes($element['products'][$key]);
    }
  }
  return $element;
}
function _uc_option_image_modify_attributes(&$element) {
  $nid = $element['nid']['#value'];
  foreach (element_children($element['attributes']) as $aid) {
    $uc_attribute = $element['attributes'][$aid];
    $oids = $uc_attribute['#type'] == 'select' ? array_keys($uc_attribute['#options']) : element_children($uc_attribute);

    // add the images of each option as the element title
    $values = NULL;
    foreach ($oids as $oid) {
      $values = uc_option_image_values('uc_product', array(
        'aid' => $aid,
        'oid' => $oid,
        'pid' => $nid,
      ));
      if (empty($values['fid']) || empty($values['fid']['value'])) {
        continue;
      }
      if (in_array($uc_attribute['#type'], array(
        'radios',
        'checkboxes',
      ))) {
        if ($values && isset($values['inline_active']) && $values['inline_active']['value']) {
          $oldtitle = $element['attributes'][$aid][$oid]['#title'];
          $extratitle = theme('uc_option_image_inline', array(
            'values' => $values,
          ));
          $newtitle = $extratitle . "<span id='oldtitle_{$oid}' class='oldtitle'>{$oldtitle}</span>";
          $element['attributes'][$aid][$oid]['#title'] = $newtitle;
        }
      }
    }

    // add the image of the selected option as a prefix
    if ($values && !empty($values['selected_active']) && $values['selected_active']['value']) {
      $element['attributes'][$aid]['#prefix'] = theme('uc_option_image_selected', array(
        'values' => $values,
        'aid' => $aid,
        'pid' => $nid,
      ));
    }
  }
}

/**
 * Implements hook_uc_object_options_form_alter()
 *
 * Instead of adding the image upload field to the table rows on this form, as was done in the D6
 * version, I'm adding a separate frameset.
 *
 * This avoids having to reimplement the entire theme function for the form.
 */
function uc_option_image_form_uc_object_options_form_alter(&$form, &$form_state) {
  $type = isset($form['type']) && !empty($form['type']['#value']) ? 'uc_' . $form['type']['#value'] : NULL;
  $nid = isset($form['id']) && !empty($form['id']['#value']) ? $form['id']['#value'] : NULL;
  $attributes = uc_product_get_attributes($nid);
  if (!$attributes) {

    // no options
    return;
  }
  $form['uc_option_image'] = array(
    '#type' => 'fieldset',
    '#title' => t("Option images"),
    '#collapsible' => FALSE,
    '#collapsed' => FALSE,
  );
  $id_key = uc_option_image_key($type);
  foreach ($attributes as $aid => $uc_attribute) {
    if (empty($uc_attribute->options)) {
      continue;
    }
    $form['uc_option_image']['option_image_attribute-' . $aid] = array(
      '#type' => 'fieldset',
      '#title' => t("Option images for @attribute", array(
        '@attribute' => $uc_attribute->name,
      )),
      '#collapsible' => TRUE,
      '#collapsed' => TRUE,
    );
    foreach ($uc_attribute->options as $oid => $uc_option) {
      $object = uc_option_image_objects('uc_product', array(
        'aid' => $aid,
        $id_key => $nid,
        'oid' => $oid,
      ));
      $option_defaults = uc_option_image_values('uc_product', array(
        'aid' => $aid,
        $id_key => $nid,
        'oid' => $oid,
      ));
      $form['uc_option_image']['option_image_attribute-' . $aid]['option_image-fid-' . $oid] = uc_option_image_file_form($type, $object, $option_defaults);
      $form['uc_option_image']['option_image_attribute-' . $aid]['option_image-fid-' . $oid]['#title'] = t("Image for !option", array(
        '!option' => $uc_option->name,
      ));
    }
    $object = uc_option_image_objects('uc_product', array(
      'aid' => $aid,
      $id_key => $nid,
    ));
    $defaults = uc_option_image_values('uc_product', array(
      'aid' => $aid,
      $id_key => $nid,
    ));
    $form['uc_option_image']['option_image_attribute-' . $aid]['display_options'] = uc_option_image_display_form('uc_product', $object, $defaults, $aid);
  }
  $form['#submit'][] = 'uc_option_image_product_options_submit';
}
function uc_option_image_product_options_submit($form, $form_state) {
  $values = $form_state['values'];
  $type = 'uc_' . $values['type'];
  $id_key = uc_option_image_key($type);
  foreach ($values['attributes'] as $aid => $uc_attribute) {
    foreach ($uc_attribute['options'] as $oid => $uc_option) {
      if (!$uc_option['select']) {
        continue;
      }
      $data['fid'] = !empty($values['option_image-fid-' . $oid]) ? $values['option_image-fid-' . $oid] : 0;
      foreach (array(
        'inline',
        'selected',
      ) as $mode) {
        $data[$mode . '_active'] = $values[$mode . '_active_' . $aid];
        $data[$mode . '_style'] = $values[$mode . '_style_' . $aid];
      }
      uc_option_image_save_form_data($type, array(
        'aid' => $aid,
        'oid' => $oid,
        $id_key => $values['id'],
      ), $data);
    }
  }
}

/**
 * Implements hook_form_uc_attribute_form_alter()
 *
 * Add a form element to allow the admin to insert a default image which applies to
 * all options, unless an option is overridden.
 */
function uc_option_image_form_uc_attribute_form_alter(&$form, &$form_state) {
  uc_option_image_form($form, $form_state, 'uc_attribute');
}
function uc_option_image_form_uc_attribute_option_form_alter(&$form, &$form_state) {
  uc_option_image_form($form, $form_state, 'uc_option');
}

/**
 * Add a form element to allow the admin to insert a default image which applies to
 * all options, unless an option is overridden.
 *
 * @param &$form
 *
 * @param &$form_state
 *
 * @param $type
 * 'attribute' or 'option'
 */
function uc_option_image_form(&$form, &$form_state, $type) {
  $aid = isset($form['aid']) ? $form['aid']['#value'] : NULL;
  $oid = isset($form['oid']) ? $form['oid']['#value'] : NULL;
  $object = uc_option_image_objects($type, array(
    'aid' => $aid,
    'oid' => $oid,
  ));
  $values = uc_option_image_values($type, array(
    'aid' => $aid,
    'oid' => $oid,
  ));
  $form['option_image'] = array(
    '#type' => 'fieldset',
    '#title' => t("Image"),
    '#collapsible' => TRUE,
    'uc_option_image_type' => array(
      '#type' => 'hidden',
      '#value' => $type,
    ),
    'uc_option_image_aid' => array(
      '#type' => 'hidden',
      '#value' => $aid,
    ),
    'uc_option_image_oid' => array(
      '#type' => 'hidden',
      '#value' => $oid,
    ),
    'uc_option_image_fid' => uc_option_image_file_form($type, $object, $values),
    uc_option_image_display_form($type, $object, $values),
  );
  if ($type != 'uc_attribute' && !empty($values['fid']) && $values['fid']['is_default']) {
    $file = file_load($values['fid']['value']);
    $form['option_image']['uc_option_image_fid']['#description'] .= "&nbsp;<strong>Currently using default image '" . $file->filename . "'</strong>";
  }
  $form['#submit'][] = 'uc_option_image_form_submit';
}

/**
 * Generate a form element for the display / image style parameters
 *
 * @param $type = ''
 * uc_attribute, uc_option, uc_product or uc_class
 *
 * @param $object
 * The data for this instance
 *
 * @param $values
 * The values for this instance, including default data
 *
 * @param $delta = ''
 * Optionally pass a delta if there are more than one of these in the form.
 *
 * @return
 * A form fieldset
 */
function uc_option_image_display_form($type, $object, $values, $delta = '') {
  $form = array(
    '#type' => 'fieldset',
    '#title' => t("Image display options"),
    '#collapsible' => TRUE,
    '#collapsed' => TRUE,
  );
  $image_styles = uc_option_image_styles($type);
  foreach (uc_option_image_modes() as $mode) {
    $active = $mode . '_active';
    $style = $mode . '_style';
    $active_key = $active;
    $style_key = $style;
    if ($delta !== '') {
      $active_key .= '_' . $delta;
      $style_key .= '_' . $delta;
    }
    if ($mode == 'inline') {
      $description = t("Display an image inline next to each option title.");
    }
    else {
      $description = t("Display an image showing the selected option(s).");
    }
    $form[$mode . '_wrapper'] = array(
      '#type' => 'fieldset',
      '#title' => t("!type display", array(
        '!type' => ucwords($mode),
      )),
      '#collapsible' => FALSE,
      '#collapsed' => FALSE,
      $active_key => array(
        '#type' => 'checkbox',
        '#title' => $description,
        '#default_value' => $object ? $object->{$active} : FALSE,
      ),
      $style_key => array(
        '#title' => t('Style'),
        '#type' => 'select',
        '#options' => $image_styles,
        '#default_value' => $object ? $object->{$style} : 'none',
        '#description' => t('Choose a style that will be used to display the image for this option, or leave blank to use the default style.'),
      ),
    );
    if ($type != 'uc_attribute' && !empty($values[$active]) && $values[$active]['is_default']) {
      $form[$mode . '_wrapper'][$active_key]['#description'] = "&nbsp;<strong>Currently active due to the default setting.</strong>";
    }
    if ($type != 'uc_attribute') {
      $form[$mode . '_wrapper'][$style]['#options']['none'] = t('use default');
      if (!empty($values[$style]) && $values[$style]['is_default']) {
        $form[$mode . '_wrapper'][$style_key]['#description'] .= "&nbsp;<strong>Currently using default style '" . $values[$style]['value'] . "'</strong>";
      }
    }
  }
  return $form;
}

/**
 * Generate a file field for uploading an attribute image.
 *
 * @param $type = ''
 * uc_attribute, uc_option, uc_product or uc_class
 *
 * @param $object
 * The data for this instance
 *
 * @param $values
 * The values for this instance, including default data
 *
 * @return
 * A form file field
 */
function uc_option_image_file_form($type, $object, $values) {
  $form = array(
    '#title' => t('Image'),
    '#type' => 'managed_file',
    '#description' => t('Upload an image that will be displayed along with this option, or leave blank to use the default image.'),
    '#default_value' => $object ? $object->fid : '',
    '#upload_location' => 'public://uc_option_image/',
  );
  if ($type != 'uc_attribute' && !empty($values['fid']) && $values['fid']['is_default']) {
    $file = file_load($values['fid']['value']);
    if (!empty($file)) {
      $form['#description'] .= "&nbsp;<strong>Currently using default image '" . $file->filename . "'</strong>";
    }
  }
  return $form;
}

/**
 * Submit handler for the attribute / option default settings forms
 */
function uc_option_image_form_submit($form, $form_state) {
  $values = $form_state['values'];
  $data['fid'] = $values['uc_option_image_fid'];
  foreach (array(
    'inline',
    'selected',
  ) as $mode) {
    $data[$mode . '_active'] = $values[$mode . '_active'];
    $data[$mode . '_style'] = $values[$mode . '_style'];
  }
  $type = $values['uc_option_image_type'];

  // if this is a new aid or oid, our value will be NULL but uc_attribute will have already
  // saved the new object and stored the new id in $values
  $aid = !empty($values['uc_option_image_aid']) ? $values['uc_option_image_aid'] : $values['aid'];
  $oid = !empty($values['uc_option_image_oid']) ? $values['uc_option_image_oid'] : !empty($values['oid']) ? $values['oid'] : NULL;
  uc_option_image_save_form_data($type, array(
    'aid' => $aid,
    'oid' => $oid,
  ), $data);
}
function uc_option_image_save_form_data($type, $ids, $values) {
  $object = uc_option_image_objects($type, $ids);
  if (!$object) {
    $object = (object) array(
      'type' => $type,
    );
    foreach ($ids as $key => $id) {
      $object->{$key} = $id;
    }
    $object->fid = 0;
  }
  $id_key = uc_option_image_key($type);

  // delete a file if it's been removed
  if (isset($values['fid']) && $values['fid'] != $object->fid) {
    if ($object->fid) {
      $file = file_load($object->fid);
      if ($file) {
        file_usage_delete($file, 'uc_option_image', $type, $object->{$id_key});
        file_delete($file);
      }
    }
    if ($values['fid'] > 0) {
      $file = file_load($values['fid']);
      if ($file) {
        $file->status = 1;
        file_save($file);
        file_usage_add($file, 'uc_option_image', $type, $object->{$id_key});
      }
    }
    $object->fid = $values['fid'];
  }
  foreach (uc_option_image_modes() as $mode) {
    $a = $mode . '_active';
    $s = $mode . '_style';

    // fixed: isset was '!empty', which prohibited unchecked checkboxes to be saved
    if (isset($values[$a]) && !empty($values[$s])) {
      $object->{$a} = $values[$a];
      $object->{$s} = $values[$s] == 'none' ? '' : $values[$s];
    }
  }
  uc_option_image_object_save($object);
}

/**
 * Retrieve the image id for a given object
 *
 * @param $type = ''
 * uc_attribute, uc_option, uc_product or uc_class
 * If this is an empty string, all objects will be returned, keyed by type
 *
 * @param $ids = NULL
 * The instance ids of the object, since each row contains multiple ids, according to the specificity of the data
 * If this is NULL, all objects of the given type will be returned
 *
 * @return
 * Depending on the parameters, an array, a keyed array, or an object
 *
 *  $type and $ids are empty:  a keyed array where the keys are type names and the values are arrays of objects
 *  only $ids is empty: an array of objects
 *  neither are empty: a single object
 *
 * In all cases, if no data is present, NULL will be returned
 */
function uc_option_image_objects($type = '', $ids = NULL) {
  $query = db_select('uc_option_image', 'uoi');
  $query
    ->fields('uoi');
  if ($type) {
    $query
      ->condition('type', $type);
  }
  if ($ids) {
    $ids += array(
      'aid' => NULL,
      'oid' => NULL,
      'pid' => NULL,
      'cid' => NULL,
    );
    foreach ($ids as $field => $id) {
      if ($id) {
        $query
          ->condition($field, $id);
      }
      else {
        $query
          ->condition($field, 0);
      }
    }
  }
  $result = $query
    ->execute();
  $objects = array();
  foreach ($result as $row) {
    $objects[$row->type][] = $row;
  }
  if (!$objects) {
    return NULL;
  }
  if ($type && $ids) {
    return empty($objects[$type]) ? NULL : $objects[$type][0];
  }
  if ($type && !$ids) {
    return empty($objects[$type]) ? NULL : $objects[$type];
  }
  return $objects;
}

/**
 * Store one or more objects to the database
 *
 * Note that the object MUST have an id.
 *
 * @param $objects
 * An array of objects, or a single object
 */
function uc_option_image_object_save($objects) {
  if (is_object($objects)) {
    $objects = array(
      $objects,
    );
  }
  foreach ($objects as $object) {
    $d = db_delete('uc_option_image');
    $d
      ->condition('type', $object->type);
    foreach (array(
      'aid',
      'oid',
      'pid',
      'cid',
    ) as $field) {
      if (!empty($object->{$field})) {
        $d
          ->condition($field, $object->{$field});
      }
      else {
        $object->{$field} = 0;
      }
    }
    $d
      ->execute();
    if (empty($object->fid) && empty($object->inline_style) && empty($object->inline_active) && empty($object->selected_style) && empty($object->selected_active)) {
      continue;
    }
    drupal_write_record('uc_option_image', $object);
  }
}

/**
 * Get the value to use for this instance
 *
 * @param $type
 * uc_attribute, uc_option, uc_product or uc_class
 *
 * @param $ids
 * The instance ids of the object, since each row contains multiple ids, according to the specificity of the data
 * If this is NULL, all objects of the given type will be returned
 *
 * @return
 * An array containing keys corresponding to the requested values.  Each key's value is itself an array, containing value and is_default.
 */
function uc_option_image_values($type, $ids) {
  $query = db_select('uc_option_image', 'uoi');
  $query
    ->fields('uoi');
  foreach ($ids as $field => $id) {
    if ($id) {
      $or = db_or();
      $or
        ->condition($field, $id);
      $or
        ->condition($field, 0);
      $query
        ->condition($or);
    }
  }
  $result = $query
    ->execute();
  $objects = array();
  foreach ($result as $row) {
    $objects[$row->type][] = $row;
  }
  $return = array();
  foreach (array(
    'fid',
    'inline_active',
    'inline_style',
    'selected_active',
    'selected_style',
  ) as $value) {
    if (!empty($objects[$type]) && !empty($objects[$type][0]->{$value})) {
      $return[$value] = array(
        'value' => $objects[$type][0]->{$value},
        'is_default' => FALSE,
      );
    }
    else {
      foreach (array(
        'uc_product',
        'uc_class',
        'uc_option',
        'uc_attribute',
      ) as $test_type) {
        if (!empty($objects[$test_type]) && !empty($objects[$test_type][0]->{$value})) {
          $return[$value] = array(
            'value' => $objects[$test_type][0]->{$value},
            'is_default' => TRUE,
          );
          break;
        }
      }
    }
  }
  return $return;
}

/**
 * Get the id key for a given type
 *
 * @param $type
 * uc_attribute, uc_option, uc_product or uc_class
 *
 * @return
 * A string corresponding to the name of the id field
 */
function uc_option_image_key($type) {
  return substr($type, 3, 1) . 'id';
}

/**
 * Get a list of image styles as options for a select element
 *
 * @param $type
 * uc_attribute, uc_option, uc_product or uc_class
 *
 * @return
 * A keyed array of image styles.  The first option is a null value
 */
function uc_option_image_styles($type) {
  $image_styles = image_styles();
  $styles = array(
    'none' => $type == 'uc_attribute' ? t("no style selected") : t('use default'),
  );
  foreach ($image_styles as $name => $settings) {
    $styles[$name] = $name;
  }
  return $styles;
}

/**
 * Get a list of display styles as options for a select element
 *
 * @return
 * A keyed array of display methods.  The first option is a null value
 */
function uc_option_image_displays() {
  return array(
    'default' => t("use default"),
    'inline' => t('inline'),
    'selected' => t('selected'),
  );
}

/**
 * Get an array of the available display modes.
 *
 * @return
 * An array of strings.
 */
function uc_option_image_modes() {
  return array(
    'inline',
    'selected',
  );
}

Functions

Namesort descending Description
uc_option_image_add_to_cart_prerender Add the image to each option element.
uc_option_image_displays Get a list of display styles as options for a select element
uc_option_image_display_form Generate a form element for the display / image style parameters
uc_option_image_file_form Generate a file field for uploading an attribute image.
uc_option_image_form Add a form element to allow the admin to insert a default image which applies to all options, unless an option is overridden.
uc_option_image_form_alter Implements hook_form_alter().
uc_option_image_form_submit Submit handler for the attribute / option default settings forms
uc_option_image_form_uc_attribute_form_alter Implements hook_form_uc_attribute_form_alter()
uc_option_image_form_uc_attribute_option_form_alter
uc_option_image_form_uc_object_options_form_alter Implements hook_uc_object_options_form_alter()
uc_option_image_key Get the id key for a given type
uc_option_image_kit_add_to_cart_prerender
uc_option_image_modes Get an array of the available display modes.
uc_option_image_objects Retrieve the image id for a given object
uc_option_image_object_save Store one or more objects to the database
uc_option_image_permission Implements hook_permission().
uc_option_image_product_options_submit
uc_option_image_save_form_data
uc_option_image_styles Get a list of image styles as options for a select element
uc_option_image_theme Implements hook_theme().
uc_option_image_uc_attribute_option_ajax
uc_option_image_values Get the value to use for this instance
_uc_option_image_modify_attributes