You are here

commerce_ss.admin.inc in Commerce Stock 7.2

Administrative callbacks and form builder functions for Commerce Stock.

File

modules/commerce_ss/includes/commerce_ss.admin.inc
View source
<?php

/**
 * @file
 * Administrative callbacks and form builder functions for Commerce Stock.
 */

/**
 * Commerce Stock admin form.
 */
function commerce_ss_admin_form($form, &$form_state) {

  // Find out what our status is. Use both the state of existing fields
  // and the state of variables to determine what's right.
  $field_name = 'commerce_stock';
  $field = field_info_field($field_name);
  $form['#tree'] = TRUE;
  $form['product_types'] = array(
    '#type' => 'fieldset',
    '#title' => t('Enable stock management for these product types'),
    '#description' => t('Note that disabling stock management removes the Stock field from the product type, deleting any existing stock data for that product type.'),
  );
  $form['product_types_override'] = array(
    '#type' => 'fieldset',
    '#title' => t('Enable stock management override for these product types'),
    '#description' => t('Note that disabling stock management override removes the Stock override field from the product type, deleting any existing stock override data for that product type.'),
  );

  // Create a checkbox for each product type, set with the current stock-
  // enabled state.
  foreach (commerce_product_types() as $type => $product_type) {
    $instance[$type] = field_info_instance('commerce_product', 'commerce_stock', $type);
    $enabled[$type] = !empty($instance[$type]);
    $form['product_types'][$type] = array(
      '#type' => 'checkbox',
      '#default_value' => $enabled[$type],
      '#title' => t('@name (@machine_name)', array(
        '@name' => $product_type['name'],
        '@machine_name' => $type,
      )),
    );
    if ($enabled[$type]) {
      $instance[$type] = field_info_instance('commerce_product', 'commerce_stock_override', $type);
      $enabled[$type] = !empty($instance[$type]);
      $form['product_types_override'][$type] = array(
        '#type' => 'checkbox',
        '#default_value' => $enabled[$type],
        '#title' => t('Allow stock override for @name (@machine_name)', array(
          '@name' => $product_type['name'],
          '@machine_name' => $type,
        )),
      );
    }
  }

  // Add a checkbox that requires them to say "I do", but don't show it
  // (#access == FALSE) unless they're deleting.
  if (!empty($form_state['commerce_stock']['delete_instances'])) {
    $type_plural = format_plural(count($form_state['commerce_stock']['delete_instances']), t('type'), t('types'));
    $affirmation = t('I understand that all stock data will be permanently removed from the product @type_plural %product_types.', array(
      '@type_plural' => $type_plural,
      '%product_types' => implode(', ', $form_state['commerce_stock']['delete_instances']),
    ));
  }
  $form['confirmation'] = array(
    '#type' => 'checkbox',
    '#title' => !empty($affirmation) ? $affirmation : '',
    '#default_value' => FALSE,
    '#access' => FALSE,
  );
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Submit'),
  );

  // If they're deleting, show the confirmation checkbox.
  if (!empty($form_state['commerce_stock']['delete_instances'])) {
    $form['confirmation']['#access'] = TRUE;
    drupal_set_message(t('You must click the confirmation checkbox to confirm that you want to delete stock data'), 'warning');
  }
  return $form;
}

/**
 * Form validator.
 *
 * If they are deleting and have not checked the confirmation
 * checkbox, make them do so.
 */
function commerce_ss_admin_form_validate($form, &$form_state) {
  if (!empty($form_state['commerce_stock']['delete_instances']) && empty($form_state['values']['confirmation'])) {
    form_set_error('confirmation', t('Please check the "I understand" checkbox to indicate you understand that all stock data in these fields will be deleted: %fields.', array(
      '%fields' => implode(', ', $form_state['commerce_stock']['delete_instances']),
    )));
  }
}

/**
 * Add or remove the Stock field from product types.
 */
function commerce_ss_admin_form_submit($form, &$form_state) {
  $form_state['commerce_stock']['delete_instances'] = array();

  // Prepare a batch in case we need it for enabling stock on product types.
  $batch = array(
    'operations' => array(),
    'file' => drupal_get_path('module', 'commerce_ss') . '/includes/commerce_ss.admin.inc',
    'finished' => 'commerce_ss_batch_product_type_init_finished',
    'title' => t('Product stock initialization'),
    'init_message' => t('Product stock initialization is starting.'),
    'progress_message' => t('Processed @current out of @total.'),
    'error_message' => t('Product stock initialization has encountered an error.'),
  );
  foreach ($form_state['values']['product_types'] as $type => $enable) {
    $instance = field_info_instance('commerce_product', 'commerce_stock', $type);
    $currently_enabled = commerce_ss_product_type_enabled($type);

    // If they want us to enable it and it doesn't currently exist, do the work.
    if ($enable && !$currently_enabled) {

      // Create the instance.
      commerce_ss_admin_create_instance('commerce_stock', 'number_decimal', TRUE, 'commerce_product', $type, t('Stock'));
      drupal_set_message(t('Stock field has been added to the %type product type.', array(
        '%type' => $type,
      )));

      // Add the operation to process this type to the batch.
      $batch['operations'][] = array(
        'commerce_ss_batch_product_type_init_process',
        array(
          $type,
          0,
        ),
      );
    }
    elseif (!$enable && $currently_enabled) {

      // If they haven't clicked the "confirm" checkbox, rebuild and get them
      // to do it.
      if (empty($form_state['values']['confirmation'])) {
        $form_state['commerce_stock']['delete_instances'][] = $type;
        $form_state['rebuild'] = TRUE;
      }
      else {

        // Remove the instance.
        field_delete_instance($instance);

        // Remove override if enabled.
        if (commerce_ss_product_type_override_enabled($type)) {
          $override = field_info_instance('commerce_product', 'commerce_stock_override', $type);
          field_delete_instance($override);
        }
        drupal_set_message(t('Stock management has been disabled on the %type product type', array(
          '%type' => $type,
        )));
      }
    }
  }
  if (!empty($form_state['values']['product_types_override'])) {
    foreach ($form_state['values']['product_types_override'] as $type => $enable) {
      $instance = field_info_instance('commerce_product', 'commerce_stock_override', $type);
      $currently_enabled = commerce_ss_product_type_override_enabled($type);
      $stock_enabled = commerce_ss_product_type_enabled($type);

      // If they want us to enable it and it doesn't currently exist,
      // do the work.
      if ($enable && $stock_enabled && !$currently_enabled) {
        commerce_ss_admin_create_instance('commerce_stock_override', 'list_boolean', FALSE, 'commerce_product', $type, t('Disable stock for this product'));
        drupal_set_message(t('Stock management override has been enabled on the %type product type', array(
          '%type' => $type,
        )));
      }
      elseif (!$enable && $currently_enabled) {

        // If they haven't clicked the "confirm" checkbox, rebuild and get them
        // to do it.
        if (empty($form_state['values']['confirmation'])) {
          $form_state['commerce_stock']['delete_instances'][] = $type;
          $form_state['rebuild'] = TRUE;
        }
        else {

          // Remove the instance.
          field_delete_instance($instance);
          drupal_set_message(t('Stock management override has been disabled on the %type product type', array(
            '%type' => $type,
          )));
        }
      }
    }
  }

  // If our batch has operations, run it now.
  if (count($batch['operations'])) {
    batch_set($batch);
  }
}

/**
 * Batch Operation Callback.
 *
 * @param string $type
 *   The product type to process.
 * @param int $init_stock_value
 *   The initial value to give to the stock field.
 */
function commerce_ss_batch_product_type_init_process($type, $init_stock_value, &$context) {
  if (!isset($context['sandbox']['progress'])) {
    $context['sandbox']['progress'] = 0;

    // Get the products to operate on.
    $result = db_query("SELECT product_id FROM {commerce_product} cp WHERE cp.type = :type", array(
      ':type' => $type,
    ));
    $context['sandbox']['product_data'] = $result
      ->fetchAll();
    $context['sandbox']['max'] = count($context['sandbox']['product_data']);
  }

  // We can safely process 10 at a time without a timeout.
  $limit = 25;
  $current_count = 0;
  while ($current_count < $limit && count($context['sandbox']['product_data'])) {

    // Update our counts.
    $current_count++;
    $context['sandbox']['progress']++;

    // ddl("$current_count < $limit, progress: "
    // . $context['sandbox']['progress']);
    // Load the product and get its wrapper.
    $product_data = array_shift($context['sandbox']['product_data']);
    $product = commerce_product_load($product_data->product_id);
    $wrapper = entity_metadata_wrapper('commerce_product', $product);

    // @todo: the name of the stock field should probably be a variable for commerce_stock v2
    $wrapper->commerce_stock = $init_stock_value;
    $wrapper
      ->save();
    $context['message'] = t('Processing product type %type %progress% complete.', array(
      '%type' => $type,
      '%progress' => intval($context['sandbox']['progress'] / $context['sandbox']['max'] * 100),
    ));
  }

  // product processing loop
  // Inform the batch engine that we are not finished,
  // and provide an estimation of the completion level we reached.
  if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
    $context['finished'] = $context['sandbox']['progress'] >= $context['sandbox']['max'];
  }
}

/**
 * Batch 'finished' callback.
 */
function commerce_ss_batch_product_type_init_finished($success, $results, $operations) {
  if ($success) {

    // We display the number of things we processed...
    drupal_set_message(t('All stock levels have been initialized to zero'));

    // @codingStandardsIgnoreStart
    // @todo: get the batch process to provide more meaningfull information
    // using $context['results']
    //    drupal_set_message(t('@count results processed.', array(
    //      '@count' => count($results),
    //    )));
    //    foreach ($results as $result) {
    //      drupal_set_message($result);
    //    }
    // @codingStandardsIgnoreEnd
  }
  else {

    // An error occurred.
    // $operations contains the operations that remained unprocessed.
    $error_operation = reset($operations);
    drupal_set_message(t('An error occurred while processing @operation with arguments : @args', array(
      '@operation' => $error_operation[0],
      '@args' => print_r($error_operation[0], TRUE),
    )));
  }
}

/**
 * Ensures a stock field is present on a product type bundle.
 */
function commerce_ss_admin_configure_product_type($type) {
  commerce_ss_admin_create_instance('commerce_stock', 'number_decimal', TRUE, 'commerce_product', $type, t('Stock'));
}

/**
 * Creates a required instance of a stock field on the specified bundle.
 *
 * @param string $field_name
 *   The name of the field; if it already exists, a new instance of the existing
 *   field will be created. For fields governed by the Commerce modules, this
 *   should begin with commerce_.
 * @param string $entity_type
 *   The type of entity the field instance will be attached to.
 * @param string $bundle
 *   The bundle name of the entity the field instance will be attached to.
 * @param string $label
 *   The label of the field instance.
 * @param int $weight
 *   The default weight of the field instance widget and display.
 */
function commerce_ss_admin_create_instance($field_name, $field_type, $required, $entity_type, $bundle, $label, $description = NULL, $weight = 0) {

  // @codingStandardsIgnoreStart
  // If a field type we know should exist isn't found, clear the Field cache.
  //  if (!field_info_field_types('commerce_stock')) {
  //    field_cache_clear();
  //  }
  // @codingStandardsIgnoreEnd
  // Look for or add the specified stock field to the requested entity bundle.
  $field = field_info_field($field_name);
  $instance = field_info_instance($entity_type, $field_name, $bundle);
  if (empty($field)) {
    $field = array(
      'field_name' => $field_name,
      'type' => $field_type,
      'cardinality' => 1,
      'entity_types' => array(
        $entity_type,
      ),
      'translatable' => FALSE,
      'locked' => FALSE,
    );
    if ($field_type == 'list_boolean') {
      $field['settings'] = array(
        'allowed_values' => array(
          0,
          1,
        ),
        'allowed_values_function' => '',
      );
    }
    $field = field_create_field($field);
  }
  if (empty($instance)) {
    $instance = array(
      'field_name' => $field_name,
      'entity_type' => $entity_type,
      'bundle' => $bundle,
      'label' => $label,
      'required' => $required,
      'settings' => array(),
      'display' => array(),
      'description' => $description,
      'default_value' => array(
        array(
          'value' => "0",
        ),
      ),
    );
    if ($field_type == 'list_boolean') {
      $instance['widget'] = array(
        'type' => 'options_onoff',
        'settings' => array(
          'display_label' => TRUE,
        ),
      );
    }
    $entity_info = entity_get_info($entity_type);

    // Spoof the default view mode so its display type is set.
    $entity_info['view modes']['default'] = array();
    field_create_instance($instance);
  }
}

Functions

Namesort descending Description
commerce_ss_admin_configure_product_type Ensures a stock field is present on a product type bundle.
commerce_ss_admin_create_instance Creates a required instance of a stock field on the specified bundle.
commerce_ss_admin_form Commerce Stock admin form.
commerce_ss_admin_form_submit Add or remove the Stock field from product types.
commerce_ss_admin_form_validate Form validator.
commerce_ss_batch_product_type_init_finished Batch 'finished' callback.
commerce_ss_batch_product_type_init_process Batch Operation Callback.