Hello fellow Commercistas,

I need to sell file access for non-premium users but show file downloads for premium users.

As far as I understand I can either choose to use the "Add to cart" field formatter or the "Rendered product", but I haven't found a way to display either one depending on user role or permission. It would also be nice to show a direct download link for authenticated non-premium users that already have bought a file license.

How could that be done? Do I need to write my own field formatter or is there a better way to achieve it?

Thanks a lot

PS: found this related issue #1266132: Add to Cart OR Show files formatter that is quite old and only about part two. But it seems there's no way around coding my own field formatter, right?

Comments

Daniel Wentsch created an issue. See original summary.

Daniel Wentsch’s picture

Issue summary: View changes
Daniel Wentsch’s picture

Daniel Wentsch’s picture

Half a year later I stumbled across the same issue and somehow managed to make my own formatter that works the way I want:
Show a Download link for users who have bought a file or have permission to bypass and show a add-to-cart form for everybody else.

For future reference here's the code:

https://gist.github.com/klickreflex/f917479dca40211a7624962cf76efaa8

/** 
 * @file
 * adds a formatter for commerce file fields that shows an add to cart button OR 
 * a file download link, depending on whether the current user has a license for the file
 *
 */
/**
 * Implements hook_field_formatter_info().
 */
function si_formatters_field_formatter_info() {
  return array(
    'add_to_cart_or_download_formatter' => array( // machine name of the formatter
      'label' => t('Add to Cart or Download'),
      'field types' => array('commerce_product_reference'), // this will only be available for product reference fields
      'settings' => array (
        // custom setting for the Download link
        'download_text' => t('Download now'), //ditto
        // Settings from the original add to cart form:
        'show_quantity' => FALSE,
        'default_quantity' => 1,
        'combine' => TRUE,
        'show_single_product_attributes' => FALSE,
        'line_item_type' => 'product',
      ), // Array of the settings we'll create
    ),
  );
}
/**
 * Implements hook_field_formatter_settings_form().
 */
function si_formatters_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
  //This gets the view_mode where our settings are stored
  $display = $instance['display'][$view_mode];
  //This gets the actual settings
  $settings = $display['settings'];
  //Initialize the element variable
  $element = array();
  $element['download_text'] = array(
    '#type'           => 'textfield',                        // Use a textbox
    '#title'          => t('Download Now'),                      // Widget label
    '#description'    => t('The download link text for users who are allowed to download without checkout.'),  // helper text
    '#default_value'  => $settings['download_text'],               // Get the value if it's already been set
  );  
  $element['show_quantity'] = array(
    '#type' => 'checkbox',
    '#title' => t('Display a textfield quantity widget on the add to cart form.'),
    '#default_value' => $settings['show_quantity'],
  );
  $element['default_quantity'] = array(
    '#type' => 'textfield',
    '#title' => t('Default quantity'),
    '#default_value' => $settings['default_quantity'] <= 0 ? 1 : $settings['default_quantity'],
    '#element_validate' => array('commerce_cart_field_formatter_settings_form_quantity_validate'),
    '#size' => 16,
  );
  $element['combine'] = array(
    '#type' => 'checkbox',
    '#title' => t('Attempt to combine like products on the same line item in the cart.'),
    '#description' => t('The line item type, referenced product, and data from fields exposed on the Add to Cart form must all match to combine.'),
    '#default_value' => $settings['combine'],
  );
  $element['show_single_product_attributes'] = array(
    '#type' => 'checkbox',
    '#title' => t('Show attribute widgets even if the Add to Cart form only represents one product.'),
    '#description' => t('If enabled, attribute widgets will be shown on the form with the only available options selected.'),
    '#default_value' => $settings['show_single_product_attributes'],
  );
  return $element;
}
/**
 * Implements hook_field_formatter_settings_summary().
 */
function si_formatters_field_formatter_settings_summary($field, $instance, $view_mode) {
  $display = $instance['display'][$view_mode];
  $settings = $display['settings'];
  $summary = t('Use either Add to Cart button or a Download link (depending on file license)'); 
  return $summary;
}
/**
 * Implements hook_field_formatter_view().
 */
function si_formatters_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
  if ($display['type'] == 'add_to_cart_or_download_formatter') {
    $result = array();
  
    $settings       = $display['settings']; // get the settings
    $download_text  = $settings['download_text']; // The label assigned in settings
    // Collect the list of product IDs.
    $product_ids = array();
    foreach ($items as $delta => $item) {
      $product_ids[$item['product_id']] = $item['product_id'];
    }
    // Exit now if we didn't find any product IDs.
    if (empty($product_ids)) {
      return;
    }
    // Load the referenced products.
    $products = commerce_product_load_multiple($product_ids, array('status' => 1));
  
    foreach ($products as $delta => $product) {
      // Protect ourselves from recursive rendering.
      static $depth = 0;
      $depth++;
      if ($depth > 20) {
        throw new CommerceProductReferenceRecursiveRenderingException(t('Recursive rendering detected when rendering product (@product_id). Aborting rendering.', array('@product_id' => $item['product_id'])));
      }
      $fid = $product->commerce_file['und'][0]['fid'];
      $file = file_load($fid);
      // the file description is used for the download link, 
      // so we set/override it here with the display setting
      $file->description = $download_text;
      $download_link = theme('file_link', array( 'file' => $file ));
      $element[0]['#markup'] = $download_link;
    }
    global $has_license;
    $has_license = false;
    if(commerce_file_access('download', $file)) {
      $has_license = true;
      return $element;
    }
  
    else {
      // show add to cart button if user has no license
      $settings = array_merge(field_info_formatter_settings($display['type']), $display['settings']);
      // Collect the list of product IDs.
      $product_ids = array();
      foreach ($items as $delta => $item) {
        if (isset($item['product_id'])) {
          $product_ids[] = $item['product_id'];
        }
        elseif (module_exists('entityreference') && isset($item['target_id'])) {
          $product_ids[] = $item['target_id'];
        }
      }
      // Load the referenced products.
      $products = commerce_product_load_multiple($product_ids);
      
      // Check to ensure products are referenced, before returning results.
      if (!empty($products)) {
        $type = !empty($settings['line_item_type']) ? $settings['line_item_type'] : 'product';
        $line_item = commerce_product_line_item_new(commerce_product_reference_default_product($products), $settings['default_quantity'], 0, array(), $type);
        $line_item->data['context']['product_ids'] = array_keys($products);
        $line_item->data['context']['add_to_cart_combine'] = !empty($settings['combine']);
        $line_item->data['context']['show_single_product_attributes'] = !empty($settings['show_single_product_attributes']);
        $result[] = array(
          '#arguments' => array(
            'form_id' => commerce_cart_add_to_cart_form_id($product_ids),
            'line_item' => $line_item,
            'show_quantity' => $settings['show_quantity'],
          ),
        );
      }
    return $result;
    }
  } // if add_to_cart_formatter
}
/**
 * Implements hook_field_attach_view_alter().
 *
 * When a field is formatted for display, the display formatter does not know
 * what view mode it is being displayed for. Unfortunately, the Add to Cart form
 * display formatter needs this information when displaying product reference
 * fields on nodes to provide adequate context for product field replacement on
 * multi-value product reference fields. This hook is used to transform a set of
 * arguments into a form using the arguments and the extra context information
 * gleaned from the parameters passed into this function.
 */
function si_formatters_field_attach_view_alter(&$output, $context) {
  
    // Loop through the fields passed in looking for any product reference fields
    // formatted with the Add to Cart form display formatter.
    foreach ($output as $field_name => $element) {
      if (!empty($element['#formatter']) && $element['#formatter'] == 'add_to_cart_or_download_formatter') {
        global $has_license;
        if(!$has_license) {
          // Prepare the context information needed by the cart form.
          $cart_context = $context;
          // Remove the full entity from the context array and put the ID in instead.
          list($entity_id, $vid, $bundle) = entity_extract_ids($context['entity_type'], $context['entity']);
          $cart_context['entity_id'] = $entity_id;
          unset($cart_context['entity']);
          // Remove any Views data added to the context by views_handler_field_field.
          // It unnecessarily increases the size of rows in the cache_form table for
          // Add to Cart form state data.
          if (!empty($cart_context['display']) && is_array($cart_context['display'])) {
            unset($cart_context['display']['views_view']);
            unset($cart_context['display']['views_field']);
            unset($cart_context['display']['views_row_id']);
          }
          // Add the context for displaying product fields in the context of an entity
          // that references the product by looking at the entity this product
          // reference field is attached to.
          $cart_context['class_prefix'] = $context['entity_type'] . '-' . $entity_id;
          $cart_context['view_mode'] = $context['entity_type'] . '_' . $element['#view_mode'];
          $entity_uri = entity_uri($context['entity_type'], $element['#object']);
          foreach (element_children($element) as $key) {
            // Extract the drupal_get_form() arguments array from the element.
            $arguments = $element[$key]['#arguments'];
            // Add the display path and referencing entity data to the line item.
            if (!empty($entity_uri['path'])) {
              $arguments['line_item']->data['context']['display_path'] = $entity_uri['path'];
            }
            $arguments['line_item']->data['context']['entity'] = array(
              'entity_type' => $context['entity_type'],
              'entity_id' => $entity_id,
              'product_reference_field_name' => $field_name,
            );
            // Update the product_ids variable to point to the entity data if we're
            // referencing multiple products.
            if (count($arguments['line_item']->data['context']['product_ids']) > 1) {
              $arguments['line_item']->data['context']['product_ids'] = 'entity';
            }
            // Replace the array containing the arguments with the return value of
            // drupal_get_form(). It will be rendered when the rest of the object is
            // rendered for display.
            $output[$field_name][$key] = drupal_get_form($arguments['form_id'], $arguments['line_item'], $arguments['show_quantity'], $cart_context);
          }
        }
      }
    }
  //} // end check
}
DamienMcKenna’s picture

Version: 7.x-2.0-beta3 » 7.x-2.x-dev